Three.js場景的基本組件


1. 場景Scene

  THREE.Scene被稱為場景圖,可以用來保存所有圖形場景的必要信息。每個添加到Scene的對象,包括Scene自身都繼承自名為THREE.Object3D對象。Scene不僅僅是一個數組,還包含了場景圖樹形結構中的所有節點。

THREE.Scene最常用的方法和屬性如下:

<!DOCTYPE html>

<html>

<head>
    <title>Example 02.01 - Basic Scene</title>
    <script type="text/javascript" src="./three.js"></script>
    <script type="text/javascript" src="./stats.js"></script>
    <script type="text/javascript" src="./dat.gui.js"></script>
    <style>
        body {
            /* 將margin設為0,overflow設為hidden,全屏顯示 */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<div id="WebGL-output">
</div>

<script type="text/javascript">

    // 一旦所有東西都被加載,我們運行我們的Three.js東西
    function init() {

        var stats = initStats();

        // 創建一個場景,其中包含所有元素,如對象、相機和燈光。
        var scene = new THREE.Scene();

        // 創建一個攝像頭,它定義了我們看的地方。
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
        scene.add(camera);

        // 創建一個渲染和設置大小
        var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        // 創建接地面
        var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // 旋轉並定位平面
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = 0;
        plane.position.z = 0;

        // 將plane對象添加到場景中
        scene.add(plane);

        // 將攝像機定位並指向場景中心
        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);

        // 添加微妙的環境照明
        var ambientLight = new THREE.AmbientLight(0x0c0c0c);
        scene.add(ambientLight);

        // 為陰影添加聚光燈
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40, 60, -10);
        spotLight.castShadow = true;
        scene.add(spotLight);

        // 將渲染器的輸出添加到html元素中
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        // 調用render函數
        var step = 0;

        var controls = new function () {
            this.rotationSpeed = 0.02;
            this.numberOfObjects = scene.children.length;

            this.removeCube = function () {
                var allChildren = scene.children;
                var lastObject = allChildren[allChildren.length - 1];
                if (lastObject instanceof THREE.Mesh) {
                    scene.remove(lastObject);
                    this.numberOfObjects = scene.children.length;
                }
            };

            this.addCube = function () {

                var cubeSize = Math.ceil((Math.random() * 3));
                var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
                var cubeMaterial = new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff});
                var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
                cube.castShadow = true;
                cube.name = "cube-" + scene.children.length;


                // 在場景中隨機放置立方體

                cube.position.x = -30 + Math.round((Math.random() * planeGeometry.parameters.width));
                cube.position.y = Math.round((Math.random() * 5));
                cube.position.z = -20 + Math.round((Math.random() * planeGeometry.parameters.height));

                // 將立方體添加到場景中
                scene.add(cube);
                this.numberOfObjects = scene.children.length;
            };

            this.outputObjects = function () {
                console.log(scene.children);
            }
        };

        var gui = new dat.GUI();
        gui.add(controls, 'rotationSpeed', 0, 0.5);
        gui.add(controls, 'addCube');
        gui.add(controls, 'removeCube');
        gui.add(controls, 'outputObjects');
        gui.add(controls, 'numberOfObjects').listen();

        render();

        function render() {
            stats.update();

            // 繞軸旋轉立方體
            scene.traverse(function (e) {
                if (e instanceof THREE.Mesh && e != plane) {

                    e.rotation.x += controls.rotationSpeed;
                    e.rotation.y += controls.rotationSpeed;
                    e.rotation.z += controls.rotationSpeed;
                }
            });

            // 渲染使用requestAnimationFrame
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms

            // 左上的對齊
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init
</script>
</body>
</html>

結果:

解釋:

  上面addCube方法給每個添加的正方體添加了一個name屬性,  cube.name = "cube-" + scene.children.length;  將名稱設為"cube-"加上scene的物體的數量(scene.children.length),name屬性對於調試非常重要們可以通過scene.getObjectByName(name)獲取到物體,然后進行其他移動位置等操作。在dat.GUI界面中用 numberOfObjects 實時的檢測scene的物體(這種方式也可以實現監測作用) 

   點擊GUI中的removeCube會調用下面方法移除最后一個添加進去的物體:並且代碼中檢查該對象是不是THREE.Mesh對象,這樣做的原因是避免移除攝像機和光源。當我們移除對象之后需要更新場景中的numberOfObjects 

this.removeCube = function () {
                var allChildren = scene.children;
                var lastObject = allChildren[allChildren.length - 1];
                if (lastObject instanceof THREE.Mesh) {
                    scene.remove(lastObject);
                    this.numberOfObjects = scene.children.length;
                }
            };

控制界面的最后一個按鈕的標簽是 outputObjects,這個函數的作用是在console控制台打印所有對象信息,這樣做對於代碼調試是非常有用的,對於在有name屬性的情況下調試代碼非常有用。例如已經知道name值為cube-5,也可使用  console.log(scene.getObjectByName("cube-5")); 打印結果如下:

霧化效果

  使用fog屬性可以為整個場景添加霧化效果。霧化效果就是場景中的物體離得越遠就會變得越模糊。

  添加霧化非常簡單,如下:

 var scene = new THREE.Scene();
 scene.fog = new THREE.Fog(0xffffff, 0.015, 100);

效果:

 

2. 相機

  Three.js提供了兩種相機,正交投影攝像機(THREE.OrthographicCamera)和透視攝像機(PerspectiveCamera),大多數使用的是透視攝像機,更接近於現實世界。

2.1  透視攝像機(PerspectiveCamera)

  參數如下:

  

這些屬性組合到一起影響你所看到的景象,如圖:

攝像機的fov屬性決定了橫向視場。基於aspect屬性,縱向視場也就相應地確定了。near屬性決定了近面距離,far屬性決定了遠面距離。近面距離和遠面距離之間的區域將會被渲染。

2.2 正交投影攝像機(THREE.OrthographicCamera)

   正交投影攝像機渲染出的物體大小都是一樣,所以不關心使用什么長寬比或者以什么樣的視角來觀察場景。當使用正交投影攝像機時,你要定義一個被渲染的方塊區域。如下屬性:

  

總結起來如下:

2.3 攝像機聚焦在指定點上

  在前面介紹了攝像機需要放在場景中的某個位置,以及攝像機能夠看到的區域將會被渲染。通常來說,攝像機會指向場景的中心,用坐標表示就是position(0,0,0)。但是改變攝像機的位置非常容易,如下:

 camera.lookAt(new THREE.Vector3(x,y,z));

  當然你還可以使攝像機追隨場景中某個物體。由於THREE.Mesh對象的位置都是THREE.Vector3對象,所以可以使用camera.lookAt(mesh.position)使攝像機指向某個物體。

 3. 渲染器(基於攝像機的角度來計算場景對象在瀏覽器中會渲染成什么樣子。)

 Three.js提供了許多的渲染器,如下:

  CanvasRenderer
  DOMRenderer
  SVGRenderer
  WebGLRenderer
  WebGLRenderTarget
  WebGLRenderTargetCube
  WebGLShaders

  使用最多的是WebGLRenderer,new THREE.WebGLRenderer()來新建一個WebGL渲染器。下面介紹其主要的參數:

antialias:

  值:true/false

  含義:是否開啟反鋸齒,設置為true開啟反鋸齒。

precision:

  值:highp/mediump/lowp

  含義:着色精度選擇。

alpha:

  值:true/false

  含義:是否可以設置背景色透明。

premultipliedAlpha:

  值:true/false

  含義:?

stencil:

  值:true/false

  含義:?

preserveDrawingBuffer:

  值:true/false

  含義:是否保存繪圖緩沖,若設為true,則可以提取canvas繪圖的緩沖。

maxLights:

  值:數值int

  含義:最大燈光數,我們的場景中最多能夠添加多少個燈光。

制定渲染器的寬高,我們用renderer.setSize(width,height)來設置;

而且一般采用下面直接設置為窗口大小:

 renderer.setSize(window.innerWidth, window.innerHeight); 

追加生成的canvas元素到容器元素中。canvas對象的獲取方法為renderer.domElement;

 document.getElementById("WebGL-output").appendChild(renderer.domElement);

設置canvas背景色(clearColor)和背景色透明度(clearAlpha),renderer.setClearColor(clearColor,clearAlpha);

renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));

 4.幾何體和網格

  在前面已經使用了幾何體和網格。比如向場景中添加球體時代碼如下:

        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); 

  我們使用 THREE.SphereGeometry(4, 20, 20) 定義了物體的形狀、使用 THREE.MeshLambertMaterial 定義了物體的外觀和材質,並將他們合並成能添加到場景中的網格(THREE.Mesh)。

 Three.js提供的幾何體如下:

 代碼如下:

復制代碼
<!DOCTYPE html>

<html>

<head>
    <title>Example 02.04 - Geometries</title>
    <script type="text/javascript" src="../libs/three.js"></script>
    <script type="text/javascript" src="../libs/ParametricGeometries.js"></script>
    <script type="text/javascript" src="../libs/ConvexGeometry.js"></script>

    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">

    // once everything is loaded, we run our Three.js stuff.
    function init() {

        var stats = initStats();

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        var scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render and set the size
        var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        // create the ground plane
        var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // rotate and position the plane
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = 0;
        plane.position.z = 0;

        // add the plane to the scene
        scene.add(plane);

        // position and point the camera to the center of the scene
        camera.position.x = -50;
        camera.position.y = 30;
        camera.position.z = 20;
        camera.lookAt(new THREE.Vector3(-10, 0, 0));

        // add subtle ambient lighting
        var ambientLight = new THREE.AmbientLight(0x090909);
        scene.add(ambientLight);

        // add spotlight for the shadows
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40, 40, 50);
        spotLight.castShadow = true;
        scene.add(spotLight);

        // add geometries
        addGeometries(scene);

        // add the output of the renderer to the html element
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        // call the render function
        var step = 0;


        render();


        function addGeometries(scene) {
            var geoms = [];

            geoms.push(new THREE.CylinderGeometry(1, 4, 4));

            // basic cube
            geoms.push(new THREE.BoxGeometry(2, 2, 2));

            // basic spherer
            geoms.push(new THREE.SphereGeometry(2));

            geoms.push(new THREE.IcosahedronGeometry(4));

            // create a convex shape (a shape without dents)
            // using a couple of points
            // for instance a cube
            var points = [
                new THREE.Vector3(2, 2, 2),
                new THREE.Vector3(2, 2, -2),
                new THREE.Vector3(-2, 2, -2),
                new THREE.Vector3(-2, 2, 2),
                new THREE.Vector3(2, -2, 2),
                new THREE.Vector3(2, -2, -2),
                new THREE.Vector3(-2, -2, -2),
                new THREE.Vector3(-2, -2, 2)
            ];
            geoms.push(new THREE.ConvexGeometry(points));

            // create a lathgeometry
            //http://en.wikipedia.org/wiki/Lathe_(graphics)
            var pts = [];//points array - the path profile points will be stored here
            var detail = .1;//half-circle detail - how many angle increments will be used to generate points
            var radius = 3;//radius for half_sphere
            for (var angle = 0.0; angle < Math.PI; angle += detail)//loop from 0.0 radians to PI (0 - 180 degrees)
                pts.push(new THREE.Vector3(Math.cos(angle) * radius, 0, Math.sin(angle) * radius));//angle/radius to x,z
            geoms.push(new THREE.LatheGeometry(pts, 12));

            // create a OctahedronGeometry
            geoms.push(new THREE.OctahedronGeometry(3));

            // create a geometry based on a function
            geoms.push(new THREE.ParametricGeometry(THREE.ParametricGeometries.mobius3d, 20, 10));

            //
            geoms.push(new THREE.TetrahedronGeometry(3));

            geoms.push(new THREE.TorusGeometry(3, 1, 10, 10));

            geoms.push(new THREE.TorusKnotGeometry(3, 0.5, 50, 20));

            var j = 0;
            for (var i = 0; i < geoms.length; i++) {
                var cubeMaterial = new THREE.MeshLambertMaterial({wireframe: true, color: Math.random() * 0xffffff});

                var materials = [

                    new THREE.MeshLambertMaterial({color: Math.random() * 0xffffff, shading: THREE.FlatShading}),
                    new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})

                ];

                var mesh = THREE.SceneUtils.createMultiMaterialObject(geoms[i], materials);
                mesh.traverse(function (e) {
                    e.castShadow = true
                });

                //var mesh = new THREE.Mesh(geoms[i],materials[i]);
                //mesh.castShadow=true;
                mesh.position.x = -24 + ((i % 4) * 12);
                mesh.position.y = 4;
                mesh.position.z = -8 + (j * 12);

                if ((i + 1) % 4 == 0) j++;
                scene.add(mesh);
            }

        }

        function render() {
            stats.update();

            // render using requestAnimationFrame
            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms

            // Align top-left
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init


</script>
</body>
</html>
復制代碼

    在Three.js中幾何體基本上是三維空間中的點集(也被稱為定點)和將這些點連接起來的面。以立方體為例:

一個立方體有8個頂點、6個面。在Three.js中每個面都是包含3個頂點的三角形,所以立方體的每個面都是由兩個三角形組成的。

比如自定義幾何體的方法如下:

復制代碼
        var vertices = [
            new THREE.Vector3(1, 3, 1),
            new THREE.Vector3(1, 3, -1),
            new THREE.Vector3(1, -1, 1),
            new THREE.Vector3(1, -1, -1),
            new THREE.Vector3(-1, 3, -1),
            new THREE.Vector3(-1, 3, 1),
            new THREE.Vector3(-1, -1, -1),
            new THREE.Vector3(-1, -1, 1)
        ];

        var faces = [
            new THREE.Face3(0, 2, 1),
            new THREE.Face3(2, 3, 1),
            new THREE.Face3(4, 6, 5),
            new THREE.Face3(6, 7, 5),
            new THREE.Face3(4, 5, 1),
            new THREE.Face3(5, 0, 1),
            new THREE.Face3(7, 6, 2),
            new THREE.Face3(6, 3, 2),
            new THREE.Face3(5, 7, 0),
            new THREE.Face3(7, 2, 0),
            new THREE.Face3(1, 3, 4),
            new THREE.Face3(3, 6, 4),
        ];

        var geom = new THREE.Geometry();
        geom.vertices = vertices;
        geom.faces = faces;
        geom.computeFaceNormals();
復制代碼

效果:

 vertices數組中保存了幾何體的頂點,faces數組保存了點連接起來的三角形面。如  new THREE.Face3(0, 2, 1),  就是使用vertices數組中的點0、2、1創建而成的三角形面。有了頂點和面然后創建 Geometry 對象並將點和面賦給上面對象,最后調用  geom.computeFaceNormals();  渲染即可。

補充:Three.js可以用多種材質來創建網格

復制代碼
                var clonedGeometry = mesh.children[0].geometry.clone();
                var materials = [
                    new THREE.MeshLambertMaterial({opacity: 0.6, color: 0xff44ff, transparent: true}),
                    new THREE.MeshBasicMaterial({color: 0x000000, wireframe: true})

                ];

                var mesh2 = THREE.SceneUtils.createMultiMaterialObject(clonedGeometry, materials);
復制代碼

2. 網格對象(THREE.Mesh)的方法和屬性

  創建一個網格需要一個幾何體,以及一個或多個材質。網格對象提供的屬性和方法如下: (有通用的移動位置、縮放等功能)

  例子如下:GUI控制其屬性(scale>1為放大)

復制代碼
<!DOCTYPE html>

<html>

<head>
    <title>Example 02.06 - Mesh Properties</title>
    <script type="text/javascript" src="../libs/three.js"></script>

    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">

    // once everything is loaded, we run our Three.js stuff.
    function init() {

        var stats = initStats();

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        var scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render and set the size
        var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;

        // create the ground plane
        var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // rotate and position the plane
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 0;
        plane.position.y = 0;
        plane.position.z = 0;

        // add the plane to the scene
        scene.add(plane);

        // position and point the camera to the center of the scene
        camera.position.x = -30;
        camera.position.y = 40;
        camera.position.z = 30;
        camera.lookAt(scene.position);

        // add subtle ambient lighting
        var ambientLight = new THREE.AmbientLight(0x0c0c0c);
        scene.add(ambientLight);

        // add spotlight for the shadows
        var spotLight = new THREE.SpotLight(0xffffff);
        spotLight.position.set(-40, 60, 020);
        spotLight.castShadow = true;
        scene.add(spotLight);

        // add the output of the renderer to the html element
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        // call the render function
        var step = 0;

        var controls = new function () {
            this.scaleX = 1;
            this.scaleY = 1;
            this.scaleZ = 1;

            this.positionX = 0;
            this.positionY = 4;
            this.positionZ = 0;

            this.rotationX = 0;
            this.rotationY = 0;
            this.rotationZ = 0;
            this.scale = 1;

            this.translateX = 0;
            this.translateY = 0;
            this.translateZ = 0;

            this.visible = true;

            this.translate = function () {

                cube.translateX(controls.translateX);
                cube.translateY(controls.translateY);
                cube.translateZ(controls.translateZ);

                controls.positionX = cube.position.x;
                controls.positionY = cube.position.y;
                controls.positionZ = cube.position.z;
            }
        };

        var material = new THREE.MeshLambertMaterial({color: 0x44ff44});
        var geom = new THREE.BoxGeometry(5, 8, 3);
        var cube = new THREE.Mesh(geom, material);
        cube.position.y = 4;
        cube.castShadow = true;
        scene.add(cube);


        var gui = new dat.GUI();

        guiScale = gui.addFolder('scale');
        guiScale.add(controls, 'scaleX', 0, 5);
        guiScale.add(controls, 'scaleY', 0, 5);
        guiScale.add(controls, 'scaleZ', 0, 5);

        guiPosition = gui.addFolder('position');
        var contX = guiPosition.add(controls, 'positionX', -10, 10);
        var contY = guiPosition.add(controls, 'positionY', -4, 20);
        var contZ = guiPosition.add(controls, 'positionZ', -10, 10);

        contX.listen();
        contX.onChange(function (value) {
            cube.position.x = controls.positionX;
        });

        contY.listen();
        contY.onChange(function (value) {
            cube.position.y = controls.positionY;
        });

        contZ.listen();
        contZ.onChange(function (value) {
            cube.position.z = controls.positionZ;
        });


        guiRotation = gui.addFolder('rotation');
        guiRotation.add(controls, 'rotationX', -4, 4);
        guiRotation.add(controls, 'rotationY', -4, 4);
        guiRotation.add(controls, 'rotationZ', -4, 4);

        guiTranslate = gui.addFolder('translate');

        guiTranslate.add(controls, 'translateX', -10, 10);
        guiTranslate.add(controls, 'translateY', -10, 10);
        guiTranslate.add(controls, 'translateZ', -10, 10);
        guiTranslate.add(controls, 'translate');

        gui.add(controls, 'visible');

        render();

        function render() {
            stats.update();

            // render using requestAnimationFrame


            cube.visible = controls.visible;


            cube.rotation.x = controls.rotationX;
            cube.rotation.y = controls.rotationY;
            cube.rotation.z = controls.rotationZ;

            cube.scale.set(controls.scaleX, controls.scaleY, controls.scaleZ);


            requestAnimationFrame(render);
            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms

            // Align top-left
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }
    window.onload = init


</script>
</body>
</html>
復制代碼

  5.光源

   Three.js有許多種不同種類的光源,每種光源都有特殊的行為和用法。如下:

 1.THREE.SpotLight

  最常用的光源(特別是想用陰影效果)。可以想象為手電筒或燈籠產生的光。THREE。PointLight與SpotLight非常相似,只是PointLight不能產生陰影(也就是沒有castShadow屬性)。

  該光源具有產生光的方向和角度,如下: 

 

 創建聚光燈光源非常簡單,如下:

復制代碼
        var pointColor = "#ffffff";
        var spotLight = new THREE.SpotLight(pointColor);
        spotLight.position.set(-40, 60, -10);
        spotLight.castShadow = true;
        spotLight.shadowCameraNear = 2;
        spotLight.shadowCameraFar = 200;
        spotLight.shadowCameraFov = 30;
        spotLight.target = plane;
        spotLight.distance = 0;
        spotLight.angle = 0.4;


        scene.add(spotLight);
復制代碼

 注意: spotLight 的target屬性比較重要,此屬性可以指向某一個特定的對象。當然可以指向空間中任意一點,方法如下:

        var target = new THREE.Object3D();
        target.position = new THREE.Vector3(5, 0, 0);
        
        spotLight.target = target;

 例如:

復制代碼
<!DOCTYPE html>

<html>

<head>
    <title>Example 03.03 - Spot Light</title>
    <script type="text/javascript" src="../libs/three.js"></script>

    <script type="text/javascript" src="../libs/stats.js"></script>
    <script type="text/javascript" src="../libs/dat.gui.js"></script>
    <style>
        body {
            /* set margin to 0 and overflow to hidden, to go fullscreen */
            margin: 0;
            overflow: hidden;
        }
    </style>
</head>
<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<!-- Javascript code that runs our Three.js examples -->
<script type="text/javascript">

    // once everything is loaded, we run our Three.js stuff.
    function init() {

        var stopMovingLight = false;

        var stats = initStats();

        // create a scene, that will hold all our elements such as objects, cameras and lights.
        var scene = new THREE.Scene();

        // create a camera, which defines where we're looking at.
        var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);

        // create a render and set the size
        var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0xEEEEEE, 1.0));
        renderer.setSize(window.innerWidth, window.innerHeight);
        renderer.shadowMapEnabled = true;
        renderer.shadowMapType = THREE.PCFShadowMap;

        // create the ground plane
        var planeGeometry = new THREE.PlaneGeometry(60, 20, 1, 1);
        var planeMaterial = new THREE.MeshLambertMaterial({color: 0xffffff});
        var plane = new THREE.Mesh(planeGeometry, planeMaterial);
        plane.receiveShadow = true;

        // rotate and position the plane
        plane.rotation.x = -0.5 * Math.PI;
        plane.position.x = 15;
        plane.position.y = 0;
        plane.position.z = 0;

        // add the plane to the scene
        scene.add(plane);

        // create a cube
        var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
        var cubeMaterial = new THREE.MeshLambertMaterial({color: 0xff3333});
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
        cube.castShadow = true;

        // position the cube
        cube.position.x = -4;
        cube.position.y = 3;
        cube.position.z = 0;

        // add the cube to the scene
        scene.add(cube);

        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
        var sphereMaterial = new THREE.MeshLambertMaterial({color: 0x7777ff});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);

        // position the sphere
        sphere.position.x = 20;
        sphere.position.y = 0;
        sphere.position.z = 2;
        sphere.castShadow = true;

        // add the sphere to the scene
        scene.add(sphere);

        // position and point the camera to the center of the scene
        camera.position.x = -35;
        camera.position.y = 30;
        camera.position.z = 25;
        camera.lookAt(new THREE.Vector3(10, 0, 0));

        // add subtle ambient lighting
        var ambiColor = "#1c1c1c";
        var ambientLight = new THREE.AmbientLight(ambiColor);
        scene.add(ambientLight);

        // add spotlight for a bit of light
        var spotLight0 = new THREE.SpotLight(0xcccccc);
        spotLight0.position.set(-40, 30, -10);
        spotLight0.lookAt(plane);
        scene.add(spotLight0);


        var target = new THREE.Object3D();
        target.position = new THREE.Vector3(5, 0, 0);


        var pointColor = "#ffffff";
        var spotLight = new THREE.SpotLight(pointColor);
        spotLight.position.set(-40, 60, -10);
        spotLight.castShadow = true;
        spotLight.shadowCameraNear = 2;
        spotLight.shadowCameraFar = 200;
        spotLight.shadowCameraFov = 30;
        spotLight.target = plane;
        spotLight.distance = 0;
        spotLight.angle = 0.4;


        scene.add(spotLight);


        // add a small sphere simulating the pointlight
        var sphereLight = new THREE.SphereGeometry(0.2);
        var sphereLightMaterial = new THREE.MeshBasicMaterial({color: 0xac6c25});
        var sphereLightMesh = new THREE.Mesh(sphereLight, sphereLightMaterial);
        sphereLightMesh.castShadow = true;

        sphereLightMesh.position = new THREE.Vector3(3, 20, 3);
        scene.add(sphereLightMesh);


        // add the output of the renderer to the html element
        document.getElementById("WebGL-output").appendChild(renderer.domElement);

        // call the render function
        var step = 0;

        // used to determine the switch point for the light animation
        var invert = 1;
        var phase = 0;

        var controls = new function () {
            this.rotationSpeed = 0.03;
            this.bouncingSpeed = 0.03;
            this.ambientColor = ambiColor;
            this.pointColor = pointColor;
            this.intensity = 1;
            this.distance = 0;
            this.exponent = 30;
            this.angle = 0.1;
            this.debug = false;
            this.castShadow = true;
            this.onlyShadow = false;
            this.target = "Plane";
            this.stopMovingLight = false;

        };

        var gui = new dat.GUI();
        gui.addColor(controls, 'ambientColor').onChange(function (e) {
            ambientLight.color = new THREE.Color(e);
        });

        gui.addColor(controls, 'pointColor').onChange(function (e) {
            spotLight.color = new THREE.Color(e);
        });

        gui.add(controls, 'angle', 0, Math.PI * 2).onChange(function (e) {
            spotLight.angle = e;
        });

        gui.add(controls, 'intensity', 0, 5).onChange(function (e) {
            spotLight.intensity = e;
        });

        gui.add(controls, 'distance', 0, 200).onChange(function (e) {
            spotLight.distance = e;
        });

        gui.add(controls, 'exponent', 0, 100).onChange(function (e) {
            spotLight.exponent = e;
        });

        gui.add(controls, 'debug').onChange(function (e) {
            spotLight.shadowCameraVisible = e;
        });

        gui.add(controls, 'castShadow').onChange(function (e) {
            spotLight.castShadow = e;
        });

        gui.add(controls, 'onlyShadow').onChange(function (e) {
            spotLight.onlyShadow = e;
        });

        gui.add(controls, 'target', ['Plane', 'Sphere', 'Cube']).onChange(function (e) {
            console.log(e);
            switch (e) {
                case "Plane":
                    spotLight.target = plane;
                    break;
                case "Sphere":
                    spotLight.target = sphere;
                    break;
                case "Cube":
                    spotLight.target = cube;
                    break;
            }

        });

        gui.add(controls, 'stopMovingLight').onChange(function (e) {
            stopMovingLight = e;
        });


        render();

        function render() {
            stats.update();
            // rotate the cube around its axes
            cube.rotation.x += controls.rotationSpeed;
            cube.rotation.y += controls.rotationSpeed;
            cube.rotation.z += controls.rotationSpeed;

            // bounce the sphere up and down
            step += controls.bouncingSpeed;
            sphere.position.x = 20 + ( 10 * (Math.cos(step)));
            sphere.position.y = 2 + ( 10 * Math.abs(Math.sin(step)));

            // move the light simulation
            if (!stopMovingLight) {
                if (phase > 2 * Math.PI) {
                    invert = invert * -1;
                    phase -= 2 * Math.PI;
                } else {
                    phase += controls.rotationSpeed;
                }
                sphereLightMesh.position.z = +(7 * (Math.sin(phase)));
                sphereLightMesh.position.x = +(14 * (Math.cos(phase)));
                sphereLightMesh.position.y = 10;

                if (invert < 0) {
                    var pivot = 14;
                    sphereLightMesh.position.x = (invert * (sphereLightMesh.position.x - pivot)) + pivot;
                }

                spotLight.position.copy(sphereLightMesh.position);
            }

            // render using requestAnimationFrame
            requestAnimationFrame(render);


            renderer.render(scene, camera);
        }

        function initStats() {

            var stats = new Stats();

            stats.setMode(0); // 0: fps, 1: ms

            // Align top-left
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.left = '0px';
            stats.domElement.style.top = '0px';

            document.getElementById("Stats-output").appendChild(stats.domElement);

            return stats;
        }
    }

    window.onload = init;


</script>
</body>
</html>
復制代碼

效果:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM