《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)
相关文章
|
3月前
|
人工智能 文字识别 安全
【WAIC 2025】AI安全的攻防前线:合合信息AI鉴伪检测技术
本文记录了作者在WAIC 2025上对合合信息AI图像鉴伪技术的深度探访,涵盖人脸视频篡改检测、AIGC图像识别、文档篡改检测三大核心技术,探讨AI时代内容安全的挑战与产业落地实践,展现图像伪造检测从技术到生态的系统化演进。
253 0
|
5月前
|
存储 芯片
移动硬盘突然打不开了?别急,有办法解决
对于不少人来说,移动硬盘是存放重要文件、照片、视频和备份资料的“宝库”。但在某天,当你满怀期待地将移动硬盘插入电脑时,却发现它根本打不开,甚至连盘符都不见了。这时候,不少人第一反应就是“完了,数据是不是没了?”别急,移动硬盘打不开不等于数据彻底消失。本文将带你逐步排查问题,并分享一些切实可行的解决方法。
|
5月前
|
机器学习/深度学习 人工智能 自然语言处理
视觉感知RAG×多模态推理×强化学习=VRAG-RL
通义实验室自然语言智能团队发布并开源了VRAG-RL,一种视觉感知驱动的多模态RAG推理框架。它能像人一样“边看边想”,通过粗到细的视觉仿生感知机制,逐步聚焦关键区域,精准提取信息。VRAG-RL结合强化学习与多专家采样策略,优化检索与推理路径,在多个视觉语言基准数据集上表现出色,显著提升准确性和效率。项目已发布技术方案并开源代码,支持快速部署和二次开发。
427 11
|
9月前
|
机器学习/深度学习 人工智能 自然语言处理
AI剧本生成与动画创作:能否成为短视频创作的革命性工具?
《AI剧本生成与动画创作》解决方案结合自然语言处理与深度学习技术,能自动生成剧本并转化为动画,极大提升创作效率,降低门槛。部署过程需约3小时,适合非专业用户快速响应热点内容。尽管在创意和细节上仍存不足,但已为短视频创作者提供高效路径,值得尝试。未来,随着技术进步,AI创作工具将带来更多惊喜。
410 10
AI剧本生成与动画创作:能否成为短视频创作的革命性工具?
|
10月前
|
前端开发 Java 程序员
2025年了,PHP 还是“世界上最好的语言”吗?
“PHP是全世界最好的语言”源自2001年PHP官方文档,本为积极评价,后因PHP性能、安全等问题成为技术圈知名梗。Ruby调侃自己是程序员最好的朋友,其他语言如Go、Java、Python则低调介绍优势。前端CSS预处理语言Sass高调自称最成熟强大,Less则低调表示仅比CSS多一点。2025年TIOBE指数显示,PHP已跌至13名,Python位居第一。尽管PHP难回巅峰,但其早期辉煌仍值得怀念。
611 3
|
API Android开发 开发者
Android经典实战之使用ViewCompat来处理View兼容性问题
本文介绍Android中的`ViewCompat`工具类,它是AndroidX库核心部分的重要兼容性组件,确保在不同Android版本间处理视图的一致性。文章列举了设置透明度、旋转、缩放、平移等功能,并提供了背景色、动画及用户交互等实用示例。通过`ViewCompat`,开发者可轻松实现跨版本视图操作,增强应用兼容性。
463 5
|
网络安全 网络架构
公网IP与内网穿透与端口映射区别
公网IP(Public IP) 公网IP是全球互联网上可路由的IP地址,用于标识网络中的设备。这些IP地址是全球唯一的,可以用来访问互联网上的资源。 公网IP通常由互联网服务提供商(ISP)分配给网络中的设备,例如家庭路由器、服务器或计算机。 公网IP是公开可访问的,因此可以用来托管服务器、提供网络服务,或者访问互联网上的资源。
806 5
|
人工智能 算法 安全
智能时代的伦理困境
随着人工智能技术的迅猛发展,人类社会正面临着前所未有的伦理挑战。本文将深入探讨AI技术所带来的隐私泄露、数据安全、偏见与歧视以及失业问题等四大伦理困境,并从法律、政策和个人隐私保护等方面提出应对策略。同时,通过分析具体的AI应用案例,揭示伦理问题的复杂性和紧迫性,以期在技术与道德之间找到平衡点,确保AI技术的发展造福于全人类。
|
存储 人工智能 NoSQL
拆解LangChain的大模型记忆方案
之前我们聊过如何使用LangChain给LLM(大模型)装上记忆,里面提到对话链ConversationChain和MessagesPlaceholder,可以简化安装记忆的流程。下文来拆解基于LangChain的大模型记忆方案。
拆解LangChain的大模型记忆方案
|
传感器 监控 网络协议
WebSocket 实战:构建高效的实时应用
WebSocket 实战:构建高效的实时应用
WebSocket 实战:构建高效的实时应用