《Three.js开发指南第2版》读书笔记1

简介: 《Three.js开发指南第2版》读书笔记1!!!《Three.js开发指南第2版》读书笔记1!!!

内容来源于书籍【美】乔斯.德克森著,杨芬、赵汝达 译的《Three.js开发指南第2版》 以下为读书笔记,仅供学习

《Three.js开发指南第2版》随书源码地址:https://github.com/josdirksen/learning-threejs

第1章 使用Three.js创建你的第一个三位场景

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script src="./libs/three.js"></script>
  </head>
  <body>
    <div id="WebGL-output"> </div>
      <script></script>


 </body>
</html>
 //定义场景
      var scene = new THREE.Scene();

坐标轴

      //创建轴并添加到场景中
      var axies = new THREE.AxisHelper(20);
      scene.add(axies);

添加平面

      //定义平面 宽60,高20
      var planeGeomentry = new THREE.PlaneGeometry(60, 20);
      //材质
      var planeMaterial = new THREE.MeshBasicMaterial({
    color: 0xcccccc });
      //合并网格对象
      var plane = new THREE.Mesh(planeGeomentry, planeMaterial);
      //绕x轴旋转90度
      plane.rotation.x = -0.5 * Math.PI;
      //场景中位置设置
      plane.position.x = 15;
      plane.position.y = 0;
      plane.position.z = 0;
      //将平面对象添加到场景中
      scene.add(plane);

摄像机


      //定义摄像机
      var camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
      //设置摄像机的位置
      camera.position.x = -30;
      camera.position.y = 40;
      camera.position.z = 30;
      //摄像机指向场景中心默认(0,0,0)
      camera.lookAt(scene.position);

渲染器

      //定义渲染器
      var renderer = new THREE.WebGLRenderer();
      renderer.setClearColorHex();
      //设置场景的背景颜色
      renderer.setClearColor(new THREE.Color(0xeeeeee));
      //设置场景的大小
      renderer.setSize(window.innerWidth, window.innerHeight);
      //输出元素到对应HTML元素中
      document.getElementById('WebGL-output').appendChild(renderer.domElement);
      //渲染器使用指定的摄像机渲染场景
      renderer.render(scene, camera);

添加物体对象

 //长方体
        var cubeGeometry = new THREE.BoxGeometry(4, 4, 4);
        var cubeMaterial = new THREE.MeshBasicMaterial({
   color: 0xff0000, wireframe: true});//材质框架wireframe
        var cube = new THREE.Mesh(cubeGeometry, cubeMaterial); 
        cube.position.x = -4;
        cube.position.y = 3;
        cube.position.z = 0;
        scene.add(cube);

        //球体
        var sphereGeometry = new THREE.SphereGeometry(4, 20, 20);
        var sphereMaterial = new THREE.MeshBasicMaterial({
   color: 0x7777ff, wireframe: true});
        var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
        sphere.position.x = 20;
        sphere.position.y = 4;
        scene.add(sphere);

点光源

  //定义点光源
      var spotLight = new THREE.SpotLight(0xffffff);
      //点光源位置
      spotLight.position.set(-40, 60, -10);
      //点光源产生投影
      spotLight.castShadow = true;
      scene.add(spotLight);

材质

材质MeshBasicMaterial不会对光源有任何反应,指挥使用指定的眼色渲染物体

材质MeshLambertMaterial MeshPhongMaterial在渲染时会对光源产生反应

//没有设置光源会对象会全黑 
var cubeMaterial = new THREE.MeshLambertMaterial({
    color: 0xff0000 });

投影

//渲染器开启渲染阴影效果
 renderer.shadowMapEnabled = true;
//平面接收投影
 plane.receiveShadow = true;

//点光源产生投影
spotLight.castShadow = true;

//物体对象产生投影
cube.castShadow = true;

动画

  function renderScene() {
   
        //保持动画能够持续运行
        requestAnimationFrame(renderScene);
        renderer.render(scene, camera);
      }

帧数统计图形

  <script src="./libs/stats.js"></script>
  <div id="Stats-output"></div>
function initStats() {
   
        var stats = new Stats();
        stats.setMode(0);
        stats.domElement.style.position = 'absolute';
        stats.domElement.style.left = '0px';
        stats.domElement.style.top = '0px';
        document.getElementById('Stats-output').appendChild(stats.domElement);
        return stats;
      }

var stats = initStats();

      function renderScene() {
   
        //保持动画能够持续运行
          //帧数统计图更新
        stats.update();
        requestAnimationFrame(renderScene);
        renderer.render(scene, camera);
      }
      renderScene();

立方体旋转

  function renderScene() {
   
        //保持动画能够持续运行
        stats.update();
        //旋转立方体
        cube.rotation.x += 0.02;
        cube.rotation.y += 0.02;
        cube.rotation.z += 0.02;

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

球体弹跳

  var step = 0;
      function renderScene() {
   
        //保持动画能够持续运行
        stats.update();

//球体弹跳
        step += 0.04;
        sphere.position.x = 20 + 10 * Math.cos(step);
        sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));

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

datGUI

控制小球弹跳和立方体的旋转的速度

<script src="./libs/dat.gui.js"></script>
//js对象
      var controls = new (function () {
   
        this.rotationSpeed = 0.02;
        this.bouncingSpeed = 0.03;
      })();

      var gui = new dat.GUI();
      //速度范围在0~0.5
      gui.add(controls, 'rotationSpeed', 0, 0.5);
      gui.add(controls, 'bouncingSpeed', 0, 0.5);


function renderScene() {
   
        //保持动画能够持续运行
        stats.update();
        //旋转立方体
        let speed = controls.rotationSpeed;
        cube.rotation.x += speed;
        cube.rotation.y += speed;
        cube.rotation.z += speed;

        step += controls.bouncingSpeed;
        sphere.position.x = 20 + 10 * Math.cos(step);
        sphere.position.y = 2 + 10 * Math.abs(Math.sin(step));

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

场景自适应浏览器

 function onResize() {
   
     //屏幕长宽比调整
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
     //渲染器尺寸调整
        renderer.setSize(window.innerWidth, window.innerHeight);
      }
      window.addEventListener('resize', onResize, false);

第2章 构建Three.js场景的基本组件

环境光

 var ambientLight = new THREE.AmbientLight(0x0c0c0c);
   scene.add(ambientLight);

datGUI控制对象添加

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();

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;
                }
            });

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

THREE.Scene.Add:向场景中添加对象

THREE.Scene.Remove:移除场景中的对象

THREE.Scene.children:获取场景中的所有的子对象列表

THREE.Scene.getObjectByName:利用name属性,获取场景中特定的对象

给场景添加雾化效果

//0xffffff白色雾化效果 0.015近处的属性值 100远处的属性值 雾的浓度线性增长
        scene.fog = new THREE.Fog(0xffffff, 0.015, 100);
//另一种雾化效果,浓度0.01,浓度随距离指数增长
scene.fog=new THREE.FogExp2( 0xffffff, 0.01 );

使用overrideMaterial属性

场景中的所有物体都会使用该属性的材质,即便自身设置了材质

scene.overrideMaterial = new THREE.MeshLambertMaterial({
   color: 0xffffff});

MeshLambertMaterial创建出不发光但可以对场景中的光源产生光源的物体

THREE.Scene常用的方法和属性

方法、属性 描述
add(object) 向场景中添加对象,可创建对象组
children 返回场景中所有对象的数组,包括摄像机和光源
getObjectByName(name,recursive) 创建对象时可指定唯一标识的name,使用该刚发可以查找特定名字的对象.recursive=false在调用者子元素上查找,recursive=true在调用者的所有后台对象上查找
remove(object) object场景中对象的引用,将对象从场景中移除
traverse(function) children属性可返回场景中的所有物体,用于遍历调用者和调用者所有的后代,被调用者和每一个后代对象调用function方法
fog 为场景添加雾化效果,可产生隐藏远处物体的雾化效果
overrideMaterial 强制场景中的所有物体使用相同的材质

几何体

//构成几何体的顶点
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)使用vertices数组中的点0,2,1创建而成的三角面         
            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;
//three.js会决定每个面的法向量,法向量用于决定不同光源下的颜色
        geom.computeFaceNormals();

注意创建面的顶点时创建顺序,顶点顺序决定了某个面是面向摄像机还是背向摄像机的

创建面向摄像机的面,顶点的顺序是顺时针的,反之逆时针

对于渲染器和游戏引擎来说,使用三角形更加容易,三角形渲染起来效率更高

//render动画循环中
function render(){
   
    //....
mesh.children.forEach(function (e) {
   
    //几何体网格指向一个更新后的顶点数组
                e.geometry.vertices = vertices;
    //告诉几何对象顶点更新
                e.geometry.verticesNeedUpdate = true;
    //重新计算每个面
                e.geometry.computeFaceNormals();
            });
    //...
}

多种材质创建网格

var materials = [
            new THREE.MeshLambertMaterial({
   opacity: 0.6, color: 0x44ff44, transparent: true}),
            new THREE.MeshBasicMaterial({
   color: 0x000000, wireframe: true})

        ];

        var mesh = THREE.SceneUtils.createMultiMaterialObject(geom, materials);

//子对象都添加阴影
 mesh.children.forEach(function (e) {
   
            e.castShadow = true            
        });

复制一个对象

this.clone = function () {
   

                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);
                mesh2.children.forEach(function (e) {
   
                    e.castShadow = true
                });
//移动新创建的网格,删除之前的副本(若存在)并把这个副本添加到场景中
                mesh2.translateX(5);
                mesh2.translateZ(5);
                mesh2.name = "clone";
                scene.remove(scene.getChildByName("clone"));
                scene.add(mesh2);


            }

THREE.SceneUtils.createMultiMaterialObject为几何体添加线框

THREE.WireframeHelper()也可以添加线框

//为网格mesh添加线框,线框颜色0x000000
var helper=new THREE.WireframeHelper(mesh,0x000000);
scene.add(helper)

helper实际上THREE.Line对象,可设置线框如何显示,如helper.material.linewidth=2指定线框的宽度

网格对象的属性和方法

方法、属性 描述
position 对象相对于父对象的位置,通常父对象为THREE.Scene对象或THREE.Object3D对象
rotation 设置绕每个轴旋转的旋转弧度,ThreeJs还提供了设置相对特定轴的旋转弧度的方法:rotateX(),rotateY(),rotateZ()
scale 沿x,y,z轴缩放对象
translateX(amount) 沿x轴将对象平移amount距离
translateY(amount) 沿y轴将对象平移amount距离
translateZ(amount) 沿z轴将对象平移amount距离
visible 该属性为false时,Mesh将不会被渲染到场景中

设置position位置

cube.position.x=10
cube.position.y=3
cube.position.z=1
//一次性设置xyz坐标值
cube.position.set(10,3,1)

cube.positon=new THREE.Vector3(10,3,1)

THREE.SceneUtils.createMultiMaterialObject创建一个多材质对象,返回的是网格组

改变网格组中某个对象的位置,可看到两个不同的THREE.Mesh对象

移动网格组,它们的偏移量是一样的

设置rotation旋转

cube.rotation.x=0.5*Math.PI
cube.rotation.set(0.5*Math.PI,0,0)
cube.rotation=new THREE.Vector3(0.5*Math.PI,0,0)

使用度数(0~360)

var degree=45
var inRadians=degree*(Math.PI/180)

scale缩放

值大于1放大,反之缩小

cube.scale.x=1.5
cube.scale.set(1.5,1,1)
cube.scale=new THREE.Vector3(1.5,1,1)

移动

相对于当前位置的平移距离

cube.translateX(10);
                cube.translateY(-10);
                cube.translateZ(5);

摄像机

透视投影摄像机,对象距离摄像机越远会被渲染得越小

正交投影摄像机,所有立方体被渲染出来的尺寸都一样,对象相对于摄像机的距离不影响渲染结果

    var controls = new function () {
   
            this.perspective = "Perspective";
        //切换摄像机
            this.switchCamera = function () {
   
                if (camera instanceof THREE.PerspectiveCamera) {
   
                    //正交投影摄像机
                    camera = new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16, -200, 500);
                    camera.position.x = 120;
                    camera.position.y = 60;
                    camera.position.z = 180;
                    camera.lookAt(scene.position);
                    this.perspective = "Orthographic";
                } else {
   
                    //透视摄像机
                    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
                    camera.position.x = 120;
                    camera.position.y = 60;
                    camera.position.z = 180;

                    camera.lookAt(scene.position);
                    this.perspective = "Perspective";
                }
            };
        };

透视摄像机

new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
参数 描述
fov fov表示视场,摄像机中能看到的那部分场景,推荐默认值50
aspect(长宽比) 渲染结果的横向尺寸和纵向尺寸的比值,推荐默认值window.innerWidth / window.innerHeight
near(近面距离) 从距离摄像机多近的距离开始渲染,通常设置尽量小,从而能够渲染从摄像机位置可看到的所有物体,推荐默认值0.1
far(远面距离) 摄像机从它所在的位置能够看到多远,推荐默认值:1000
zoom(变焦) 可放缩场景,负数时场景上下颠倒,推荐默认值:1

正交投影摄像机

 new THREE.OrthographicCamera(window.innerWidth / -16, window.innerWidth / 16, window.innerHeight / 16, window.innerHeight / -16, -200, 500);
参数 描述
left(左边界) 可视范围的做屏幕,渲染不放的左边界,负值将不会看到,超过边界不会被看得
right(右边界) 可渲染区域的另一个侧面
top(上边界) 可渲染区域的最上面
bottom(下边界) 可渲染区域的最下面
near(近面距离) 基于摄像机所处位置,从这一点开始渲染场景
far(远面距离) 基于摄像机所处位置,渲染场景到这一点位置
zoom(变焦) 放缩场景

将摄像机聚焦在指定点上

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

lookAt可以追随物体,看向特定的网格

camera.lookAt(mesh.position)
相关文章
|
1月前
|
移动开发 前端开发 JavaScript
从零开始学习前端开发:入门指南
本文将介绍从零开始学习前端开发的入门指南。通过学习HTML、CSS和JavaScript等基础知识,读者将了解前端开发的基本概念和工具,并学会如何构建简单的网页应用程序。无论您是初学者还是有一定经验的开发人员,本文都将帮助您打下坚实的前端开发基础。
|
5月前
|
开发框架 数据可视化 前端开发
【Unity 3D】GameFramework、QFramework框架简介及应用实战(超详细 附源码)
【Unity 3D】GameFramework、QFramework框架简介及应用实战(超详细 附源码)
153 0
|
11月前
|
缓存 前端开发 JavaScript
《Three.js开发指南第2版》读书笔记2
《Three.js开发指南第2版》读书笔记2!!!《Three.js开发指南第2版》读书笔记2!!!
|
前端开发 JavaScript 算法
ThreeJS开篇
ThreeJS开篇
111 0
|
移动开发 JavaScript 前端开发
Three.js 入门指南
Three.js 是一个 JavaScript 库,用于在 Web 浏览器中创建 3D Web 图形。
413 0
Three.js 入门指南
|
Web App开发 前端开发 图形学
Unity实战问题-WebGL问题集锦-上篇
众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣!!!
387 1
|
算法 JavaScript
如何用webgl(three.js)搭建一个3D库房-第一课
今天我们来讨论一下如何使用当前流行的WebGL技术搭建一个库房并且实现实时有效交互 第一步、搭建一个3D库房首先你得知道库房长啥样,我们先来瞅瞅库房长啥样(这是我在网上找的一个库房图片,百度了“库房”一下,找不到合适的全景,我们也只能窥一斑思全豹了,就它了,特此声明:此图片归原作者所有 非本人所拍,...
2745 0