# three-learn **Repository Path**: silent_flute/three-learn ## Basic Information - **Project Name**: three-learn - **Description**: 学习threejs的仓库, 脚手架用的是umi3.x - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-09-14 - **Last Updated**: 2021-07-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 概览 这是一个用`umi3.x`搭建的学习`threejs`的仓库, threejs的版本是`r129` # 启动 推荐使用`yarn`管理`node`依赖 1. `$ yarn`安装依赖 2. `$ yarn start`启动项目 # 参考文章 1. [Three.js入门指南](https://www.ituring.com.cn/book/1272) - `threejs`中已经没有`CubeGeometry`这个api了, 这里我使用的是`BoxGeometry`, 效果一样 - 关于lookAt方法的定义: `C:\Users\{用户名}\AppData\Local\Microsoft\TypeScript\4.2\node_modules\@types\three\src\core\Object3D.d.ts`, 这里可以看到直接使用三个数字也是可以的: `lookAt(vector: Vector3 | number, y?: number, z?: number): void;` - `CylinderGeometry`的参数增加到了8个, 羡辙的书中是6个 - `TorusKnotGeometry`的参数现在只有6个, 书中有7个 - `TubeGeometry`参数类型有变化 - `MeshFaceMaterial`已经被废弃 - [《Three.js 入门指南》书例代码](http://zhangwenli.com/ThreeExample.js/)中的`例 7.2.1`示例代码错误地写成了`例5.1.1`的了 - 书中`例 8.1.2`和`例 8.1.3`的描述和实际代码展示的效果刚好相反, 这里我是看教程写的代码, 因此效果和教程中描述的一致, 与书中描述相反那...详情见该项目中的`ex812_ambientLight_Red`和`ex813_ambientLight_RedBlack`, 同时为了便于理解, 在命名上, 我将书中白色 绿色的物体以位置左 右来重新命名, 而非原先的颜色命名, 因为最终渲染的颜色已经和命名时候的颜色无关了 - 书中`例 8.3.1`调整了物体的`y`值, 但由于我是复制前面的案例来用, 只修改了光照, 因此物体位置上和书中的代码示例有些不同 - 另: `api`的差异记录的可能不全, 一切以`threejs`里`d.ts`文件中的写法为准 - 书中关于`阴影`的一章提到: `能形成阴影的光源只有THREE.DirectionalLight与THREE.SpotLight`, 但是我看其他教程并自己动手之后发现,`DirectionalLight PointLight SpotLight都能产生阴影` 2. [初识Three.js](https://zhuanlan.zhihu.com/p/27296011) # 相机通识 相机的位置`position`和相机看的方向`lookAt`是不一样的, 相机默认的`lookAt`是z轴的负方向 # 透视投影相机 `fov`变大, 视景体变大, 几何体相对视景体就小了, 因此看起来就小了, 注意,改变`fov`并不会引起画面横竖比例的变化,而改变`aspect`则会改变横竖比例 # 几何体 `BoxGeometry(width, height, depth, widthSegments, heightSegments, depthSegments)`, width是x方向上的长度;height是y方向上的长度;depth是z方向上的长度;后三个参数分别是在三个方向上的分段数,如widthSegments为3的话,代表x方向上水平分为三份。一般情况下不需要分段的话,可以不设置后三个参数,后三个参数的缺省值为1 物体的默认位置是原点,对于立方体而言,是其几何中心在原点的位置 注意这个分段是对六个面进行分段,而不是对立方体的体素分段,因此在立方体的中间是不分段的,只有六个侧面被分段。 # 材质 ## BasicMaterial 使用基本材质(BasicMaterial)的物体,渲染后物体的颜色始终为该材质的颜色,而不会由于光照产生明暗、阴影效果。如果没有指定材质的颜色,则颜色是随机的 使用`BasicMaterial`设置颜色之后再添加光照效果将`不再`起作用, 也就是教程中所说的: 对于基本材质,即使改变场景中的光源,使用该材质的物体也始终为颜色处处相同的效果. 要想光照效果起作用应该使用其他材质类, 比如下面的`MeshLambertMaterial`或者`MeshPhongMaterial`, 关于材质对光照效果的影响可以查看该项目中的`ex811_ambientLight` ## MeshLambertMaterial > 需要漫反射效果时使用该材质 Lambert材质(MeshLambertMaterial)是符合Lambert光照模型的材质。Lambert光照模型的主要特点是只考虑漫反射而不考虑镜面反射的效果,因而对于金属、镜子等需要镜面反射效果的物体就不适应,对于其他大部分物体的漫反射效果都是适用的。同时这个类里面有些参数, 和我们之前相对更熟悉的`BasicMaterial`有所不同: - `color`: 用来表现材质对散射光的反射能力,也是最常用来设置材质颜色的属性, 这个属性和`BasicMaterial`的一样, 除此之外,还可以用ambient和emissive控制材质的颜色, `color`也可以称之为散射光 - ~~`ambient`: 表示对环境光的反射能力, 只有当设置了`AmbientLight`后,该值才是有效的,材质对环境光的反射能力与环境光强相乘后得到材质实际表现的颜色。~~ 看到第8章的时候发现这句话不成立, 当我们同时使用`ambient`和`AmbientLight`之后, 无论`ambient`的值为多少, 实际渲染的颜色都将是`AmbientLight`的颜色, 如果需要一个材质颜色和光照的综合结果, 应该使用`color`和`AmbientLight` - `emissive`: 材质的自发光颜色,可以用来表现光源的颜色 ## MeshPhongMaterial > 需要使用镜面反射效果时使用该材质 Phong材质(MeshPhongMaterial)是符合Phong光照模型的材质。和Lambert不同的是,Phong模型考虑了镜面反射的效果,因此对于金属、镜面的表现尤为适合。由于漫反射部分与Lambert模型是一致的,因此,如果不指定镜面反射系数,而只设定漫反射,其效果与Lambert是相同的, 同时也有表示环境光和自发光的参数 - `specular`: 镜面反射的高光点的颜色, 也可以称之为镜面光 - `shininess`: 高光数, 它的值越大, 则高光光斑越小 ## MeshNormalMaterial 法向材质可以将材质的颜色设置为其法向量的方向,有时候对于调试很有帮助。(什么叫将颜色设置为方向???) 材质的颜色与照相机与该物体的角度相关 同时, 跟着文章修改照相机的位置代码发现好像产生了物体旋转了的效果, 但这是照相机的位置发生了改变, 那应该还有物体本身发生旋转的方式 我们观察的是同样的三个面,但是由于观察的角度不同,物体的颜色就不同了。因此,在调试时,要知道物体的法向量,使用法向材质就很有效 ## TextureMaterial 纹理材质, 给物体设置图片等材质, 替换普通的颜色材质, 可以使用`TextureLoader`类来加载纹理材质, 比如图片 # 光照 它会改变物体表面材质的颜色 # 重绘 一般是重新设置材质之后重绘, 或者动画效果的重绘 比如 ``` const mesh = new Mesh(geometry, material); textureLoader.loadAsync('/img/0.png').then( texture => { mesh.material = new MeshLambertMaterial({ map: texture }); renderer.render(scene, camera); } ); ``` # wrapS/wrapT 定义纹理的水平/垂直方向的环绕模式, 详见文档: [Textures](https://threejs.org/docs/#api/en/constants/Textures) # RepeatWrapping 一种环绕模式, 表示无限重复环绕, 详见文档: [Textures](https://threejs.org/docs/#api/en/constants/Textures) # requestAnimationFrame 它会根据屏幕的刷新率来决定什么时候调用我们写的动画函数, 具体可查阅文档[window.requestAnimationFrame](https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame) # stats.js 一个给我们提供实时FPS信息的库: [stats.js](https://github.com/mrdoob/stats.js), `stats.domElement.style.position = '';`可以帮我们移除它默认的`fixed`定位, 从而让我们方便地将它添加到任意元素中去 # 光照和阴影 ## 光照 真实世界中的光照很复杂, 计算机只是尽可能的去模拟, 人们提出了几种不同的光源(例如环境光, 点光源, 平行光, 聚光灯等), 灵活应用将能使渲染效果更加逼真 环境光只是设置整体, 整个空间的明暗效果, 要想物体呈现一个立体感, 需要设置具有方向性的光源, 比如点光源, 平行光 聚光灯 ### 环境光 环境光是指场景整体的光照效果,是由于场景内若干光源的多次反射形成的亮度一致的效果,通常用来为整个场景指定一个基础亮度。因此,环境光没有明确的光源位置,在各处形成的亮度也是一致的。 ### 点光源 点光源是不计光源大小,可以看作一个点发出的光源。点光源照到不同物体表面的亮度是线性递减的,因此,离点光源距离越远的物体会显得越暗。 点光源可以设置位置, 也就是设置发光的点的位置, 而照射方向则是从发光点出发向着四周照射 ### 平行光 我们都知道,太阳光常常被看作平行光,这是因为相对地球上物体的尺度而言,太阳离我们的距离足够远。对于任意平行的平面,平行光照射的亮度都是相同的,而与平面所在位置无关。 以下是个人结合教程对于平行光的理解: 位置: 平行光所处位置是平面, 是点(2, 5, 3)所在的平面, 也就是说是从点(2, 5, 3)所在的平面发出, 而不是从点(2, 5, 3)的位置发出, 否则就成点光源了 方向: 照射方向则是点(2, 5, 3)所在平面的法向量方向: [(2, 5, 3), (-2, -5, -3)], 具体可查看示例`ex831_directionalLight` ### 聚光灯 从一个点发射出来的光, 是一个圆锥体, 而且距离发光点越远, 这个椎体越大, 同时它还能投下阴影 ## 阴影 相对于获得光照多的地方, 获得光照少的地方将产生阴影 能产生阴影的光源: DirectionalLight SpotLight 能表现阴影的材质: LambertMaterial PhongMaterial 这两个光源类似正交和透视投影照相机, DirectionalLight能设置left right top bottom near far, SpotLight能设置fov near far, 可以理解为可以设置投影的视景体 # 着色器 着色器是一段在GPU中执行的接近C语言的代码,顶点着色器对于每个顶点调用一次,片元着色器对于每个片元调用一次。 具体的解释可查阅教程第9章的内容