|
| 1 | +--- |
| 2 | +draft: false |
| 3 | +title: Three.js Journey Notes 2 - Classic Techniques |
| 4 | +date: 2025-03-10 |
| 5 | +categories: Learning |
| 6 | +comments: true |
| 7 | +ShowToc: true |
| 8 | +isCJKLanguage: false |
| 9 | +--- |
| 10 | + |
| 11 | +课程链接:[three.js journey](https://threejs-journey.com/) |
| 12 | + |
| 13 | +[Notes 1 - Basics](../threejs-journey-notes-1-basics) |
| 14 | + |
| 15 | +## Lights |
| 16 | + |
| 17 | +basically walk through all lights types to demonstrate them one by one and |
| 18 | +feel what they can do. |
| 19 | + |
| 20 | +- [AmbientLight](https://threejs.org/docs/index.html#api/en/lights/AmbientLight) |
| 21 | + - applies omnidirectional(全方向的) lighting on all geometries of the scene. |
| 22 | + - good to simulate light bouncing around the scene |
| 23 | + - cannot be used to cast shadows as it does not have a direction |
| 24 | +- [DirectionalLight](https://threejs.org/docs/index.html#api/en/lights/DirectionalLight) |
| 25 | + - coming from same direction, parallel, like the Sun |
| 26 | + - can cast shadows |
| 27 | +- [HemisphereLight](https://threejs.org/docs/index.html#api/en/lights/HemisphereLight) |
| 28 | + - similar to the *AmbientLight* but with a *different color* fading from the *sky* to the color coming from the ground. |
| 29 | + - Faces facing the sky will be lit by one color while another color will lit faces facing the ground. |
| 30 | + - cannot be used to cast shadows |
| 31 | +- [PointLight](https://threejs.org/docs/index.html#api/en/lights/PointLight) |
| 32 | + - almost like a lighter, light source is infinitely small, and the light spreads uniformly in every direction |
| 33 | + - can cast shadows |
| 34 | +- [RectAreaLight](https://threejs.org/docs/index.html#api/en/lights/RectAreaLight) |
| 35 | + - like a big rectangle lights you can see on the photoshoot set, big plane lighting |
| 36 | + - can be used to simulate light sources such as bright windows or strip lighting |
| 37 | + - only works with `MeshStandardMaterial` and `MeshPhysicalMaterial` |
| 38 | + - cannot be used to cast shadows |
| 39 | +- [SpotLight](https://threejs.org/docs/index.html#api/en/lights/SpotLight) |
| 40 | + - like flashlight, 手电筒🔦聚光灯效果 |
| 41 | + - gets emitted from a single point in one direction, along a cone that increases in size the further from the light it gets |
| 42 | + - can cast shadows |
| 43 | + |
| 44 | +### Performance |
| 45 | + |
| 46 | +> Add as few lights as possible and try to use the lights that cost less |
| 47 | +
|
| 48 | +- Minimal cost: |
| 49 | + - AmbientLight |
| 50 | + - HemisphereLight |
| 51 | +- Moderate cost: |
| 52 | + - DirectionalLight |
| 53 | + - PointLight |
| 54 | +- High cost: |
| 55 | + - SpotLight |
| 56 | + - RectAreaLight |
| 57 | + |
| 58 | +Another technique is called baking, idea it that you can bake the light into the texture. |
| 59 | +So that you can have the effects on the texture as loaded, but it can't be changed when you change the scene. |
| 60 | + |
| 61 | +### Helpers |
| 62 | + |
| 63 | +helpers for positioning and orienting the lights: |
| 64 | +- HemisphereLightHelper |
| 65 | +- DirectionalLightHelper |
| 66 | +- PointLightHelper |
| 67 | +- RectAreaLightHelper |
| 68 | +- SpotLightHelper |
| 69 | + |
| 70 | + |
| 71 | +## Shadows |
| 72 | + |
| 73 | +> The back of the objects are indeed in the dark, and this is called the **core shadow**. What we are missing is the **drop shadow**, where objects create shadows on the other objects. |
| 74 | +
|
| 75 | +> When you do one render, Three.js will first do a render for each light supposed to cast shadows. Those renders will simulate what the light sees as if it was a camera. During these lights renders, MeshDepthMaterial replaces all meshes materials. |
| 76 | +
|
| 77 | +> The results are stored as textures and named shadow maps. |
| 78 | +
|
| 79 | +> You won't see those shadow maps directly, but they are used on every material supposed to receive shadows and projected on the geometry. |
| 80 | +Here's an excellent example of what the directional light and the spotlight see: https://threejs.org/examples/webgl_shadowmap_viewer.html |
| 81 | + |
| 82 | +简单复述就是 three.js 会当作 camera 使用 MeshDepthMaterial 为 base 保存每一种 light 的模拟出的结果 shadow map,然后当成 texture 渲染出来 |
| 83 | + |
| 84 | +Only 3 types of lights supports shadows: |
| 85 | +- [PointLight](https://threejs.org/docs/index.html#api/en/lights/PointLight) |
| 86 | +- [DirectionalLight](https://threejs.org/docs/index.html#api/en/lights/DirectionalLight) |
| 87 | +- [SpotLight](https://threejs.org/docs/index.html#api/en/lights/SpotLight) |
| 88 | + |
| 89 | + |
| 90 | +```js |
| 91 | +const directionalLight = new THREE.DirectionalLight(0xffffff, 1.5) |
| 92 | +directionalLight.castShadow = true; // to activate shadow |
| 93 | +console.log(directionalLight.shadow); // can be accessed directly |
| 94 | + |
| 95 | +// render size |
| 96 | +directionalLight.shadow.mapSize.width = 1024 |
| 97 | +directionalLight.shadow.mapSize.height = 1024 |
| 98 | + |
| 99 | +// add camera helper to adjust near and far |
| 100 | +directionalLight.shadow.camera.near = 1 |
| 101 | +directionalLight.shadow.camera.far = 6 |
| 102 | +const directionalLightCameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera) |
| 103 | +scene.add(directionalLightCameraHelper) |
| 104 | + |
| 105 | +directionalLight.shadow.camera.top = 2 |
| 106 | +directionalLight.shadow.camera.right = 2 |
| 107 | +directionalLight.shadow.camera.bottom = - 2 |
| 108 | +directionalLight.shadow.camera.left = - 2 |
| 109 | + |
| 110 | +// make shadow blur on edges |
| 111 | +directionalLight.shadow.radius = 10 |
| 112 | + |
| 113 | +// change shadow map algorithm |
| 114 | +renderer.shadowMap.enabled = true |
| 115 | +renderer.shadowMap.type = THREE.PCFSoftShadowMap |
| 116 | +``` |
| 117 | + |
| 118 | +Shadow map optimisations |
| 119 | +- render size |
| 120 | + - default is 512x512, we can improve it by assign bigger size like 1024x1024 (but cost more) |
| 121 | +- near and far |
| 122 | + - same as camera's near and far, can use [CameraHelper](https://threejs.org/docs/#api/en/helpers/CameraHelper) to adjust the values |
| 123 | +- amplitude |
| 124 | + - camera's amplitude |
| 125 | + - The smaller the values, the more precise the shadow will be. But if it's too small, the shadows will just be cropped |
| 126 | +- blue with `radius` property |
| 127 | +- Shadow map algorithm |
| 128 | + - **THREE.BasicShadowMap:** Very performant but lousy quality |
| 129 | + - **THREE.PCFShadowMap:** Less performant but smoother edges (default) |
| 130 | + - **THREE.PCFSoftShadowMap:** Less performant but even softer edges |
| 131 | + - **THREE.VSMShadowMap:** Less performant, more constraints, can have unexpected results |
| 132 | + |
| 133 | +Baking shadows, exactly like baking light into textures. But it's static, if the object or the light moves, the shadow won't. |
| 134 | +Alternative is to use a more simple shadow under the sphere and slightly above the plane. |
| 135 | +It's less realistic but more dynamic. |
| 136 | + |
| 137 | + |
| 138 | +## Haunted House |
| 139 | + |
| 140 | +完全跟着一步一步做的 demo, [code](https://github.com/bambooom/threejs-journey/blob/main/src/course/chapter2-classic-techniques/16-haunted-house.tsx) |
| 141 | + |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | + |
| 146 | +## Particles |
| 147 | + |
| 148 | +popular and can be used to achieve various effects such as stars, smoke, rain, dust, fire, and many other things. |
| 149 | + |
| 150 | +each particle is composed of a plane (two triangles) always facing the camera. |
| 151 | + |
| 152 | +need `BufferGeometry` and `PointsMaterial`, we need to create a [Points](https://threejs.org/docs/#api/en/objects/Points). |
| 153 | + |
| 154 | + |
| 155 | +```js |
| 156 | +// Material |
| 157 | +const particlesMaterial = new THREE.PointsMaterial({ |
| 158 | + size: 0.02, |
| 159 | + sizeAttenuation: true |
| 160 | +}); |
| 161 | + |
| 162 | +// Points |
| 163 | +const particles = new THREE.Points(particlesGeometry, particlesMaterial) |
| 164 | +scene.add(particles) |
| 165 | +``` |
| 166 | + |
| 167 | +Custom geometry like space |
| 168 | + |
| 169 | +```js |
| 170 | +// Geometry |
| 171 | +const particlesGeometry = new THREE.BufferGeometry() |
| 172 | +const count = 500 |
| 173 | + |
| 174 | +const positions = new Float32Array(count * 3) // Multiply by 3 because each position is composed of 3 values (x, y, z) |
| 175 | + |
| 176 | +for(let i = 0; i < count * 3; i++) // Multiply by 3 for same reason |
| 177 | +{ |
| 178 | + positions[i] = (Math.random() - 0.5) * 10 // Math.random() - 0.5 to have a random value between -0.5 and +0.5 |
| 179 | +} |
| 180 | + |
| 181 | +particlesGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)) // Create the Three.js BufferAttribute and specify that each information is composed of 3 values |
| 182 | + |
| 183 | + |
| 184 | +// particlesMaterial.alphaTest = 0.001 |
| 185 | +// particlesMaterial.depthTest = false |
| 186 | +particlesMaterial.depthWrite = false |
| 187 | +particlesMaterial.blending = THREE.AdditiveBlending |
| 188 | + |
| 189 | +particlesMaterial.vertexColors = true |
| 190 | +``` |
| 191 | + |
| 192 | +- alphaTest |
| 193 | + - value between 0 and 1 that enables the WebGL to know when not to render the pixel according to that pixel's transparency |
| 194 | +- depthTest |
| 195 | + - WebGL tests if what's being drawn is closer than what's already drawn |
| 196 | +- depthWrite |
| 197 | + - The depth of what's being drawn is stored in what we call a depth buffer. Instead of not testing if the particle is closer than what's in this depth buffer, we can tell the WebGL not to write particles in that depth buffer |
| 198 | +- blending |
| 199 | + - tell the WebGL not only to draw the pixel, but also to add the color of that pixel to the color of the pixel already drawn. That will have a saturation effect that can look amazing. |
| 200 | +- apply different colors |
| 201 | + |
| 202 | + |
| 203 | +make a galaxy by particles, [code](https://github.com/bambooom/threejs-journey/blob/main/src/course/chapter2-classic-techniques/18-galaxy-generator.tsx) |
| 204 | + |
| 205 | + |
| 206 | + |
| 207 | + |
| 208 | +[scroll based animation to make web page demo](https://github.com/bambooom/threejs-journey/blob/main/src/course/chapter2-classic-techniques/19-scroll-based-animation.tsx) |
0 commit comments