《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)
相关文章
|
2月前
|
JavaScript API 图形学
一个案例带你从零入门Three.js,深度好文!
【8月更文挑战第1天】本教程无需任何Threejs知识!本教程以入门为主,带你快速了解Three.js开发
77 2
一个案例带你从零入门Three.js,深度好文!
手把手教会你使用Markdown【从入门到精通一篇就够了】
手把手教会你使用Markdown【从入门到精通一篇就够了】
|
4月前
技术笔记:tcolorbox宏包简明教程
技术笔记:tcolorbox宏包简明教程
134 0
|
4月前
|
分布式计算 NoSQL Spark
技术好文:scyllaDB基本使用
技术好文:scyllaDB基本使用
|
5月前
|
Java 编译器 Go
【字节跳动青训营】后端笔记整理-1 | Go语言入门指南:基础语法和常用特性解析(一)
本文主要梳理自第六届字节跳动青训营(后端组)-Go语言原理与实践第一节(王克纯老师主讲)。
123 1
|
5月前
|
存储 Go C++
【字节跳动青训营】后端笔记整理-1 | Go语言入门指南:基础语法和常用特性解析(二)
Go 语言中的复合数据类型包括数组、切片(slice)、映射(map)和结构体(struct)。
65 0
|
5月前
|
API Android开发 C++
【字节跳动大牛系列教学】Android源码剖析之Framwork层消息传递
【字节跳动大牛系列教学】Android源码剖析之Framwork层消息传递
|
5月前
|
编解码 数据可视化 小程序
微信小游戏开发(第一篇
微信小游戏开发(第一篇
269 0
|
Android开发 Windows
Android逆向 | 基础知识篇 - 02 - 关于Android签名
Android逆向 | 基础知识篇 - 02 - 关于Android签名
|
运维 算法 数据可视化
从零开始学习R语言编程:完全指南
R语言是一种流行的数据分析语言,广泛应用于学术界、商业界和社会科学研究等领域。与其它数据分析软件相比,R语言的优点包括免费开源、高效可靠、具有强大的数据分析和可视化能力等。R语言的编程基础包括了各种控制结构和函数,可以方便地实现算法和数据操作。本篇文章将介绍R语言的基本编程结构,涵盖if-else语句、for循环、while循环和函数等,帮助读者快速上手学习R语言编程。
450 0