|
| 1 | +--- |
| 2 | +draft: false |
| 3 | +title: Three.js Journey Notes - Basics |
| 4 | +date: 2025-02-26 |
| 5 | +categories: Learning |
| 6 | +comments: true |
| 7 | +ShowToc: true |
| 8 | +isCJKLanguage: true |
| 9 | +--- |
| 10 | + |
| 11 | +课程链接:[three.js journey](https://threejs-journey.com/) |
| 12 | + |
| 13 | +前年买了一门课,去年断断续续学了一两个月,搁置了5个月,今年继续学。 |
| 14 | +先复习之前的课记个笔记。 |
| 15 | + |
| 16 | +*没有追求写得很通顺,中英胡乱混合ing* |
| 17 | + |
| 18 | +## why use Three.js |
| 19 | + |
| 20 | +> [WebGL](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) (Web Graphics Library) is a JavaScript API for rendering high-performance interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. |
| 21 | +
|
| 22 | +WebGL 是用的 GPU 来绘制的,GPU 有很多 parallel workers,所以很快。 |
| 23 | +怎么放置 points 每个 pixel 如何画都是在 shaders 里定义的。(shader 是个挺重要的概念,之后再好好了解) |
| 24 | + |
| 25 | +但是 native WebGL too hard,手写 WebGL 还是比较痛苦比较麻烦的一件事(老师举例,画一个简单的三角形就需要写 100 行代码), |
| 26 | +所以 [three.js](https://threejs.org/) 应运而生。 |
| 27 | + |
| 28 | + |
| 29 | +## 4 Basics elements |
| 30 | + |
| 31 | +- A scene that will contain objects, 对应 `THREE.Scene` |
| 32 | +- Some objects, can be many things like primitive geometrics, imported models, particles, lights, etc. |
| 33 | + - 常用的 Mesh - combination of a geometry (the shape) and a material (how it looks) |
| 34 | + - threejs 有一系列 Geometry class 对应各种形状,另一系列 Materials 就是对应样子等 |
| 35 | +- `Camera` |
| 36 | + - camera 不可见,只是一种理论上的 point of view。render 的时候就是从 camera 的视角出发去做 |
| 37 | + - 也可以有多台camera,像是拍电影一样,在 cameras 中间切换视角 |
| 38 | + - threejs 也有几种不同的 camera 可以选择,basic 选用 `PerspectiveCamera` |
| 39 | + - *field of view*: how large your vision angle is, unit in degrees and corresponds to the vertical vision angle |
| 40 | +- `Renderer`,顾名思义就是做渲染工作的,它会将当下的 scene 从 camera 的视角出发,将结果画在一张画布上 |
| 41 | + |
| 42 | +```js |
| 43 | +import * as THREE from 'three'; |
| 44 | + |
| 45 | +// Scene |
| 46 | +const scene = new THREE.Scene(); |
| 47 | +// Mesh |
| 48 | +const geometry = new THREE.BoxGeometry(1, 1, 1); // cube: width, height, depth |
| 49 | +const material = new THREE.MeshBasicMaterial({ color: 0xff0000 }); // red, using hex |
| 50 | +const mesh = new THREE.Mesh(geometry, material); |
| 51 | +scene.add(mesh); // need to add to scene |
| 52 | + |
| 53 | +const sizes = { |
| 54 | + width: 800, |
| 55 | + height: 600, |
| 56 | +}; |
| 57 | +// Camera |
| 58 | +// 75 - field of view, 5的角度在实际情况来说可能太大。但对初学者来说,比较合适,不会跟丢 objects。多数 website, around 35 |
| 59 | +// width / height - aspect ratio,画布的宽高比 |
| 60 | +const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height); |
| 61 | +// camera.position.x = 1 |
| 62 | +// camera.position.y = 1 |
| 63 | +camera.position.z = 3; // by default, everything is at the center, so need to move camera backwards to be able to see objects |
| 64 | +scene.add(camera); |
| 65 | + |
| 66 | +// Canvas,<canvas class="webgl"></canvas> |
| 67 | +const canvas = document.querySelector('canvas.webgl') |
| 68 | +// Renderer |
| 69 | +const renderer = new THREE.WebGLRenderer({ |
| 70 | + canvas: canvas |
| 71 | +}); |
| 72 | +renderer.setSize(sizes.width, sizes.height); |
| 73 | +renderer.render(scene, camera); // 这一步才会在网页上看到渲染的效果 |
| 74 | +``` |
| 75 | + |
| 76 | +## Animations |
| 77 | + |
| 78 | +前面的例子里 render 只是一个静态的结果,想要让 3D 能动起来,需要使用 `window.requestAnimationFrame` 持续进行 render |
| 79 | + |
| 80 | +fps = frames per seconds |
| 81 | + |
| 82 | +```js |
| 83 | +let time = Date.now() |
| 84 | + |
| 85 | +const tick = () => { |
| 86 | + // Time |
| 87 | + const currentTime = Date.now() |
| 88 | + const deltaTime = currentTime - time // around 16-17 milliseconds |
| 89 | + time = currentTime |
| 90 | + // update objects |
| 91 | + // 用 delta 是来fix 时间,rotation 速度就和 frame rate 不相关,在任何 frame rate 都保持相同速度 |
| 92 | + mesh.rotation.y += 0.002 * deltaTime // rotate the object |
| 93 | + // render part |
| 94 | + renderer.render(scene, camera); |
| 95 | + window.requestAnimationFrame(tick) // will repeated being called |
| 96 | +} |
| 97 | + |
| 98 | +tick() |
| 99 | +``` |
| 100 | + |
| 101 | +## Camera |
| 102 | + |
| 103 | +- `THREE.Camera` is abstract class, not supposed to being used directly |
| 104 | +- `THREE.ArrayCamera` from multiple cameras on a specific areas of render |
| 105 | +- `THREE.StereoCamera` render through 2 cameras that mimic eyes to create a parallax effect, 3D VR like |
| 106 | +- `THREE.CubeCamera` do 6 renders, surroundings like environment map, reflection or shadow map |
| 107 | +- `THREE.OrthographicCamera` render the scene without perspective, RTS game, object size is always the same, no matter far or near |
| 108 | + - parameters: left, right, top, bottom; 像是设置一个 box square,方形前进这样 |
| 109 | + - cube looks flat |
| 110 | +- `THREE.PerspectiveCamera` 最常用的 |
| 111 | + - field of view,实际上人类的视觉角度是 huge,但是在这里不能设置太大。比如设置成 140 degree 的话,objects 会看上去很小,以及 distorted(严重变形)。原因是我们一般在网页里是 render in a rectangle area,会需要将 amplified render squeeze 到一个方形里。老师推荐 45-75 fov |
| 112 | + - aspect ratio,width/height of render |
| 113 | + - near / far: objects closer than near or further than far will not show up,camera can see 的距离区间 |
| 114 | + - 如果 far 设置小于 camera 的 distance,objects 就会看不见或者部分看不见 |
| 115 | + - 但是不要使用 extreme values like 0.0001 and 9999999 to prevent z-fighting |
| 116 | + - [https://twitter.com/FreyaHolmer/status/799602767081848832](https://twitter.com/FreyaHolmer/status/799602767081848832) |
| 117 | + - [https://twitter.com/Snapman_I_Am/status/800567120765616128](https://twitter.com/Snapman_I_Am/status/800567120765616128) |
| 118 | + - 0.1 可以,具体取决于project |
| 119 | + |
| 120 | + |
| 121 | +## Geometries |
| 122 | + |
| 123 | +What: |
| 124 | +- composed of **vertices** (point coordinates) and **faces** (triangles that join vertices to create surface) |
| 125 | +- can be used for meshed and particles |
| 126 | +- can store more data than positions |
| 127 | + |
| 128 | +All inherited from `BufferGeometry`. |
| 129 | + |
| 130 | +The [BoxGeometry](https://threejs.org/docs/#api/en/geometries/BoxGeometry) has 6 parameters: |
| 131 | +- `width`: The size on the `x` axis |
| 132 | +- `height`: The size on the `y` axis |
| 133 | +- `depth`: The size on the `z` axis |
| 134 | +- `widthSegments`: How many subdivisions in the `x` axis |
| 135 | +- `heightSegments`: How many subdivisions in the `y` axis |
| 136 | +- `depthSegments`: How many subdivisions in the `z` axis |
| 137 | + |
| 138 | +segments 在需要精细化每个点的时候会用到,设置 material 的时候带上 `wireframe: true` 就可以看到 triangles segments. |
| 139 | + |
| 140 | +### How to create your own buffer geometry |
| 141 | + |
| 142 | +To add vertices to a [BufferGeometry](https://threejs.org/docs/#api/en/core/BufferGeometry) you must start with a [Float32Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float32Array). |
| 143 | +[Float32Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Float32Array) are native JavaScript typed array. You can only store floats inside, and the length of that array is fixed. |
| 144 | + |
| 145 | +```js |
| 146 | +// Create an empty BufferGeometry |
| 147 | +const geometry = new THREE.BufferGeometry() |
| 148 | +// init Float32Array |
| 149 | +const positionsArray = new Float32Array([ |
| 150 | + 0, 0, 0, // First vertex |
| 151 | + 0, 1, 0, // Second vertex |
| 152 | + 1, 0, 0 // Third vertex |
| 153 | +]) |
| 154 | +// convert Float32Array to BufferAttribute |
| 155 | +// 3 means how much values compose one vertex |
| 156 | +const positionsAttribute = new THREE.BufferAttribute(positionsArray, 3) |
| 157 | +``` |
| 158 | + |
| 159 | +## Textures |
| 160 | + |
| 161 | +mostly used types: |
| 162 | +- color |
| 163 | +- alpha |
| 164 | + - grayscale image |
| 165 | + - white visible, black not visible |
| 166 | +- height (or Displacement) 使表面不平? |
| 167 | + - grayscale image |
| 168 | + - move the vertices to create some relief |
| 169 | + - need enough subdivision |
| 170 | +- normal |
| 171 | + - add small details |
| 172 | + - doesn't need subdivision |
| 173 | + - the vertices won't move |
| 174 | + - lure(诱骗诱使) the light about the face orientation |
| 175 | + - better performances than adding a height texture with a lot if subdivision |
| 176 | +- ambient occlusion |
| 177 | + - grayscale |
| 178 | + - add fake shadows in crevices |
| 179 | +- metalness |
| 180 | + - grayscale |
| 181 | + - white is metallic, black is not |
| 182 | + - mostly for reflection |
| 183 | +- roughness |
| 184 | + - grayscale |
| 185 | + - in duo with the metalness |
| 186 | + - white is rough |
| 187 | + - black is smooth |
| 188 | + - mostly for light dissipation |
| 189 | + |
| 190 | +**PBR principle** (especially the metalness and the roughness): |
| 191 | +- physical based rendering |
| 192 | +- follow real-life directions to get realistic results |
| 193 | +- reading: |
| 194 | + - [Basic Theory of Physically-Based Rendering | Marmoset](https://marmoset.co/posts/basic-theory-of-physically-based-rendering/) |
| 195 | + - [Physically-Based Rendering, And You Can Too! | Marmoset](https://marmoset.co/posts/physically-based-rendering-and-you-can-too/) |
| 196 | + |
| 197 | + |
| 198 | +filtering and mipmapping: a technique that consists of creating half a smaller version of a texture again and again until you get a 1x1 texture. All those texture variations are sent to the GPU, and the GPU will choose the most appropriate version of the texture. |
| 199 | +主要是为了处理毛边,blurring |
| 200 | + |
| 201 | + |
| 202 | +`.jpg` lossy compression but usually lighter |
| 203 | +`.png` lossless compression but usually heavier |
| 204 | + |
| 205 | +make texture image as small as possible, GPU need to store it, but it has limitation. |
| 206 | +texture resolution better must be a power of 2, 512x512, 1024x1024, 512x2048 |
| 207 | + |
| 208 | +where to find textures: |
| 209 | +- [poliigon.com](http://poliigon.com/) |
| 210 | +- [3dtextures.me](http://3dtextures.me/) |
| 211 | +- [arroway-textures.ch](http://arroway-textures.ch/) |
| 212 | + |
| 213 | +## Material |
| 214 | + |
| 215 | +are used to put a color on each visible pixel of the geometries |
| 216 | +The algorithms that decide on the color of each pixel are written in programs called **shaders**. Writing shaders is one of the most challenging parts of WebGL and Three.js, but don't worry; Three.js has many built-in materials with pre-made shaders. |
| 217 | + |
| 218 | +Normals material: |
| 219 | +"Normals" are information encoded in each vertex that contains the direction of the outside of the face |
| 220 | + |
| 221 | +Huge library of matcap |
| 222 | +[GitHub - nidorx/matcaps: Huge library of matcap PNG textures organized by color](https://github.com/nidorx/matcaps) |
| 223 | +create matcap online studio |
| 224 | +[Matcap Tweaker](https://www.kchapelier.com/matcap-studio/) |
| 225 | + |
| 226 | +Environment map is like an image of what's surrounding the scene. |
| 227 | +example picture is found from [Poly Haven](https://polyhaven.com/) |
| 228 | + |
| 229 | +clearcoat, varnish glass effect on the top of the material |
0 commit comments