ThreeJs - 160萬個三角形組成的正方體

效果圖如下:代碼如下(代碼還需要優化,暫時能實現效果就行)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>160萬個幾何體</title>
    <style>
        body{
            color: #cccccc;
            font-family: monospace;
            font-size: 13px;
            text-align: center;

            background-color: #FF8C00;
            margin: 0px;
            overflow: hidden;
        }

        #info{
            position: absolute;
            top: 0px;
            width: 100%;
            padding: 5px;
        }

        a{
            color: cornflowerblue;
        }
    </style>
</head>
<body>
    <div id="container"></div>
    <div id="info">WEBGL_ThreeJs:高性能渲染保持高幀數</div>

    <script src="../../js/three.js"></script>
    <script src="../../js/Stats.js"></script>
    <script src="../../js/WEBGL.js"></script>
    <script>
        //檢測瀏覽器是否支持webgl
        if( !WEBGL.isWebGLAvailable() ){
            var warning = WEBGL.getWebGLErrorMessage();
            document.getElementById('container').appendChild(warning);
        }

        //執行函數
        initAll();
        animate();

        //聲明常量
        var container,states;
        var renderer,camera,scene;
        var mesh;
        
        //初始化容器
        function initContainer() {
            container = document.getElementById("container");
        }

        //初始化相機
        function initCamera() {
            camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,1,3500);
            //相機位置
            camera.position.x = 0;
            camera.position.y = 0;
            camera.position.z = 2800;
            //相機快門座標
            camera.up.x = 0;
            camera.up.y = 1;
            camera.up.z = 0;
            //鏡頭的朝向
            camera.lookAt(0,0,1);
        }
        //初始化場景
        function initScene() {
            scene = new THREE.Scene();
            scene.fog = new THREE.Fog(0x050505,2000,3500);
        }

        //初始化燈光
        function initLight() {
            //環境光
            var lightAm = new THREE.AmbientLight(0x444444,1.0);
            scene.add(lightAm);

            //方向光
            var lightDi1 = new THREE.DirectionalLight(0xffffff,0.5);
            lightDi1.position.set(1,1,1);
            scene.add(lightDi1);

            var lightDi2 = new THREE.DirectionalLight(0xffffff,1.5);
            lightDi2.position.set(0,-1,0);
            scene.add(lightDi2);
        }

       function initObject() {
           //定義三角形
           var triangles = 160000;
           //模型多,使用buffer更高效
           var geometry = new THREE.BufferGeometry();
           //形成三角形的點的個數,
           var positions = new Float32Array(triangles*3*3);
           //每個頂點一個法向量,也可以每個面一個法向量
           var normals = new Float32Array(triangles*3*3);
           //每個頂點一個顏色
           var colors = new Float32Array(triangles*3*3);
           //實例化顏色
           var color = new THREE.Color();

           //正方體的邊長爲800
           var n = 800;
           //正方體邊長的一半
           var n2 = n/2;
           //三角形邊長
           var d = 12;
           //三角形邊長的一半
           var d2 = d / 2;

           //聲明三角形頂點的位置
           var pA = new THREE.Vector3();
           var pB = new THREE.Vector3();
           var pC = new THREE.Vector3();

           //a-b\c-b的兩條線
           var ab = new THREE.Vector3();
           var cb = new THREE.Vector3();

           for(var i = 0; i < positions.length; i += 9){
               //隨機每個座標的位置
               var x = Math.random() * n - n2;
               var y = Math.random() * n - n2;
               var z = Math.random() * n - n2;

               //隨機每個點的位置
               var ax = x + Math.random() * d -d2;
               var ay = y + Math.random() * d -d2;
               var az = z + Math.random() * d -d2;

               var bx = x + Math.random() * d -d2;
               var by = y + Math.random() * d -d2;
               var bz = z + Math.random() * d -d2;

               var cx = x + Math.random() * d -d2;
               var cy = y + Math.random() * d -d2;
               var cz = z + Math.random() * d -d2;

               positions[ i ]    = ax;
               positions[ i + 1] = ay;
               positions[ i + 2] = az;

               positions[ i + 3] = bx;
               positions[ i + 4] = by;
               positions[ i + 5] = bz;

               positions[ i + 6] = cx;
               positions[ i + 7] = cy;
               positions[ i + 8] = cz;

               //座標放入vector中
               pA.set(ax,ay,az);
               pB.set(bx,by,bz);
               pC.set(cx,cy,cz);

               //使用向量的減法算出ab\cb向量
               ab.subVectors(pA,pB);
               cb.subVectors(pC,pB);

               //計算出垂直於屏幕的向量,也稱之爲法線。根據ab、cb計算,返回值會賦給被調用的函數名(用詞不太對)
               cb.cross(ab);
               //法線的歸一化(按比例縮短到單位長度,方向不變,也就是說不管這條線有多長,都縮短到1,方向不變)
               cb.normalize();

               //法線
               var nx = cb.x;
               var ny = cb.y;
               var nz = cb.z;

               normals[ i ] = nx;
               normals[ i + 1 ] = ny;
               normals[ i + 2 ] = nz;

               normals[ i + 3 ] = nx;
               normals[ i + 4 ] = ny;
               normals[ i + 5 ] = nz;

               normals[ i + 6 ] = nx;
               normals[ i + 7 ] = ny;
               normals[ i + 8 ] = nz;

               //顏色
               var vx = ( x / n ) + 0.5;
               var vy = ( y / n ) + 0.5;
               var vz = ( z / n ) + 0.5;
               //設置RGB通道值
               color.setRGB(vx , vy , vz);

               colors[ i ] = color.r;
               colors[ i + 1 ] = color.g;
               colors[ i + 2 ] = color.b;

               colors[ i + 3 ] = color.r;
               colors[ i + 4 ] = color.g;
               colors[ i + 5 ] = color.b;

               colors[ i + 6 ] = color.r;
               colors[ i + 7 ] = color.g;
               colors[ i + 8 ] = color.b;
           }

           //把值添加到geometry中
           geometry.addAttribute('position',new THREE.BufferAttribute(positions,3));
           geometry.addAttribute('normal',new THREE.BufferAttribute(normals,3));
           geometry.addAttribute('color',new THREE.BufferAttribute(colors,3));


           //計算幾何體的外邊界球,可用作物體的碰撞或相交
           geometry.computeBoundingSphere();

           var material = new THREE.MeshPhongMaterial({
               color:0xaaaaaa , specular:0xffffff , shininess:250,
               side:THREE.DoubleSide , vertexColors:THREE.VertexColors
           });

           mesh = new THREE.Mesh(geometry,material);
           scene.add(mesh);
       }

        //初始化渲染器
        function initRenderer() {
            renderer = new THREE.WebGLRenderer({
                antialias:false
            });
            //設置渲染器的大小
            renderer.setSize(window.innerWidth,window.innerHeight);
            //清理顏色
            renderer.setClearColor(scene.fog.color);
            //設置設備像素比
            renderer.setPixelRatio(container.devicePixelRatio);
            //渲染到容器中
            container.appendChild(renderer.domElement);

            //gamma矯正,使畫面看起來舒服
            renderer.gammaInput = true;
            renderer.gammaOutput = true;

            //性能監視器
            states = new Stats();
            states.domElement.style.position = 'absolute';
            states.domElement.style.top = '0';
            container.appendChild(states.domElement);

            //容器視圖調整大小時執行函數
            window.addEventListener('resize',onContainerResite,false);
        }
        
        //視圖改變大小時被執行的函數
        function onContainerResite() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize(window.innerWidth , window.innerHeight);
        }
        
        //循環渲染
        function animate() {
            requestAnimationFrame(animate);
            render();
            states.update();
        }
        
        function render() {
            var time = Date.now() * 0.001;

            mesh.rotation.x = time * 0.25;
            mesh.rotation.y = time * 0.25;

            renderer.render(scene , camera);
        }


        //執行所有函數
        function initAll() {
            initContainer();
            initCamera();
            initScene();
            initLight();
            initObject();
            initRenderer();
        }
        

    </script>
</body>
</html>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章