5 |
6 |
7 |
8 | ThreeJS-Template
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Sahil Kandhare
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import "./styles/style.css";
2 | import * as THREE from "three";
3 | import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
4 |
5 | class ThreeJSTemplate {
6 | constructor() {
7 | this.initScene();
8 | this.initCamera();
9 | this.initRenderer();
10 | this.initLights();
11 | this.initMesh();
12 | this.initControls();
13 | this.addEventListeners();
14 | this.animate();
15 | }
16 |
17 | initScene() {
18 | this.scene = new THREE.Scene();
19 | this.clock = new THREE.Clock();
20 | }
21 |
22 | initCamera() {
23 | this.sizes = {
24 | width: window.innerWidth,
25 | height: window.innerHeight,
26 | };
27 | this.camera = new THREE.PerspectiveCamera(
28 | 75,
29 | this.sizes.width / this.sizes.height,
30 | 0.1,
31 | 100
32 | );
33 | this.camera.position.z = 3;
34 | this.scene.add(this.camera);
35 | }
36 |
37 | initRenderer() {
38 | this.canvas = document.querySelector("canvas.webgl");
39 | this.renderer = new THREE.WebGLRenderer({
40 | canvas: this.canvas,
41 | alpha: true,
42 | });
43 | this.renderer.setSize(this.sizes.width, this.sizes.height);
44 | this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
45 | }
46 |
47 | initLights() {
48 | const ambientLight = new THREE.AmbientLight(0x404040);
49 | const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
50 | const hemisphereLight = new THREE.HemisphereLight(0x7444ff, 0xff00bb, 0.5);
51 | const pointLight = new THREE.PointLight(0x7444ff, 1, 100);
52 | pointLight.position.set(0, 3, 4);
53 |
54 | this.scene.add(ambientLight, directionalLight, hemisphereLight, pointLight);
55 | }
56 |
57 | initMesh() {
58 | const geometry = new THREE.BoxGeometry(1, 1, 1);
59 | const material = new THREE.MeshStandardMaterial({ color: "#7444ff" });
60 | this.mesh = new THREE.Mesh(geometry, material);
61 | this.scene.add(this.mesh);
62 | }
63 |
64 | initControls() {
65 | this.controls = new OrbitControls(this.camera, this.canvas);
66 | this.controls.enableDamping = true;
67 | }
68 |
69 | addEventListeners() {
70 | window.addEventListener("resize", () => this.onResize());
71 | }
72 |
73 | onResize() {
74 | this.sizes.width = window.innerWidth;
75 | this.sizes.height = window.innerHeight;
76 |
77 | this.camera.aspect = this.sizes.width / this.sizes.height;
78 | this.camera.updateProjectionMatrix();
79 |
80 | this.renderer.setSize(this.sizes.width, this.sizes.height);
81 | this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
82 | }
83 |
84 | animate() {
85 | const elapsedTime = this.clock.getElapsedTime();
86 |
87 | // Animate mesh
88 | this.mesh.rotation.x = elapsedTime * 0.5;
89 | this.mesh.rotation.y = elapsedTime * 0.5;
90 | this.mesh.rotation.z = elapsedTime * 0.5;
91 |
92 | // Update controls
93 | this.controls.update();
94 |
95 | // Render
96 | this.renderer.render(this.scene, this.camera);
97 |
98 | // Call animate again on the next frame
99 | window.requestAnimationFrame(() => this.animate());
100 | }
101 | }
102 |
103 | // Initialize the template
104 | new ThreeJSTemplate();
105 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Three.js - Template
2 |
3 | Get started with Three.js to render 3d Web Experience.
4 |
5 | ## Setup
6 |
7 | Download Node.js. Run this followed commands:
8 |
9 | ```bash
10 | # Install dependencies (only the first time)
11 | npm install
12 |
13 | # Run the local server at localhost:8080
14 | npm run dev
15 |
16 | # Run the local server at locat ip
17 | npm run host
18 |
19 | # Build for production in the dist/ directory
20 | npm run build
21 | ```
22 | ## Credits
23 | - [Vite Bundler](https://vitejs.dev/)
24 |
25 | ## 🤨 What is WebGL?
26 | WebGL is a JavaScript API that renders triangles in a canvas at a remarkable speed. It's compatible with most modern browsers, and it's fast because it uses the Graphic Processing Unit (GPU) of the visitor.
27 |
28 | WebGL can draw more than triangles and can also be used to create 2D experiences, but we will focus on 3D experiences using triangles for the course's sake.
29 |
30 | The GPU can do thousands of parallel calculations. Imagine that you want to render a 3D model and this model is constituted of 1000 triangles—which, come to think about it, is not that many. Each triangle includes 3 points. When we want to render our model, the GPU will have to calculate the position of these 3000 points. Because the GPU can do parallel calculations, it will handle all the triangles points in one raw.
31 |
32 | Once the model's points are well placed, the GPU needs to draw each visible pixel of those triangles. Yet again, the GPU will handle the thousands and thousands of pixels calculations in one go.
33 |
34 | The instructions to place the points and draw the pixels are written in what we call shaders. And let me tell you, shaders are hard to master. We also need to provide data to these shaders. For example: how to place the points according to the model transformations and the camera's properties. These are called matrices. We also need to provide data to help colorize the pixels. If there is a light and the triangle is facing that light, it should be brighter than if the triangle isn't.
35 |
36 | And this is why native WebGL is so hard. Drawing a single triangle on the canvas would take at least 100 lines of code. Good luck if you want to add perspective, lights, models, and animate everything in that case.
37 |
38 | But native WebGL benefits from existing at a low level, very close to the GPU. This enables excellent optimizations and more control.
39 |
40 | ## 🤨 What is three.js ?
41 | Three.js is a cross-browser JavaScript library and application programming interface used to create and display animated 3D computer graphics in a web browser using WebGL. The source code is hosted in a [repository](https://github.com/mrdoob/three.js/) on GitHub.
42 | ### See also
43 | - [Three.js](https://en.wikipedia.org/wiki/Three.js) on Wikipedia
44 | - [Three.js official website](https://threejs.org/)
45 |
46 | Three.js is the most popular JavaScript framework for displaying three-dimensional content on the web.
47 | Three.js eliminates the need for a high-end gaming PC or console to display photorealistic 3D graphics. You don't even need to instal any software. With only a smartphone and a web browser, anyone can now experience stunning 3D applications in the palm of their hand.
48 |
49 | This incredible library, as well as the vibrant community that surrounds it, are all you need to create games, music videos, scientific and data visualisations, and pretty much anything else you can think of, right in your browser on your laptop, tablet, or smartphone!
50 |
51 |
52 |
53 | ## 💖 List of my favourite Three.js projects:
54 |
55 |
56 |
99 |
100 | ### Made with 💜 by [SahilK-027](https://github.com/SahilK-027)
101 |
--------------------------------------------------------------------------------
/GRAPHICS_THEORY.md:
--------------------------------------------------------------------------------
1 | # Theory of some important topics
2 |
3 | ## Scene graph
4 |
5 | In Three.js, a scene graph is a hierarchical data structure used to organize and manage all the objects in a 3D scene.
6 |
7 | At its core, s scene graph is like a tree structure, where:
8 |
9 | - The root is the `Scene` object.
10 | - Each node is an `Object3D` (or a subclass like `Mesh`, `Group`, `Camera`, `Light`, etc.).
11 | - You add things as child using `.add()` method.
12 |
13 | ```
14 | Ex. scene.add(camera);
15 | Ex. carGeometry.add(playerGeometry)
16 | ```
17 |
18 | - Each node can have children, forming a parent-child relationship.
19 |
20 | ### Why it's important:
21 |
22 | The scene graph lets you:
23 |
24 | - Organize complex scenes easily.
25 | - Apply transformations (position, rotation, scale) to parent objects and have them affect all children.
26 | - Efficiently render scenes by traversing the graph and drawing each object.
27 |
28 | ## Lights
29 |
30 | ### Ambient light
31 |
32 | - A global light that illuminates all objects equally, no matter where they are or how they're oriented.
33 | - Has no direction, no shadows, and no falloff.
34 | - Usage: Used as a base fill light to ensure that all parts of your scene are at least slightly visible, especially in shadowy areas.
35 |
36 | ### Hemisphere light
37 |
38 | - Similar to ambient light, that simulates the sky and ground.
39 | - Illuminates objects from above (sky color) and below (ground color).
40 | - Directionless like ambient, but gives a soft gradient feel, more natural than plain ambient light. No shadows.
41 | - Usage: Good for outdoor scenes where you want a soft, diffuse light feel from the environment.
42 |
43 | ### React area light
44 |
45 | - Emits light from a rectangular surface.
46 | - Usage: More physically accurate for things like windows, soft boxes, or ceiling panels.
47 | - Only works with `MeshStandardMaterial` and `MeshPhysicalMaterial`.
48 | - No shadow.
49 | - Needs to be manually added to the scene and oriented correctly.
50 | - Requires `RectAreaLightUniformsLib` to be initialized.
51 |
52 | ### Directional light
53 |
54 | - Simulates light from a faraway source, like the sun.
55 | - All rays are parallel, creating strong, uniform shadows.
56 | - Casts shadows and can be targeted to a specific object.
57 | - Usage: Ideal for simulating sunlight or moonlight in outdoor scenes.
58 |
59 | ### Point light
60 |
61 | - Emits light in all directions from a single point (like a light bulb).
62 | - Has distance-based falloff and can cast shadows.
63 | - Usage: Great for simulating bulbs, torches, or small light sources.
64 |
65 | ### Spotlight
66 |
67 | - Emits a cone-shaped beam of light (like a flashlight or stage light).
68 | - Has an angle, decay, distance, and can cast shadows.
69 | - Can be aimed at a specific point using .target.
70 | - Usage: Used when you need focused lighting, e.g., on characters, objects, or stages.
71 |
72 | ## Textures in 3D Graphics
73 |
74 | In 3D graphics, a texture is not just a decorative image, it's data applied to the surface of a 3D object to control specific properties of the material. Textures act as inputs that drive how a material behaves visually.
75 |
76 | ### Types of Textures & What They Control
77 |
78 | Here’s what different types of texture maps do:
79 | | Texture Type | Role / Purpose |
80 | | -------------------------- | -------------------------------------------------------------- |
81 | | **Color / Diffuse** | Defines the **base color** of a surface |
82 | | **Normal Map** | Simulates **fine surface detail** by altering lighting normals |
83 | | **Bump Map** | Similar to normal maps, but simpler, using grayscale height |
84 | | **Displacement Map** | Actually modifies **geometry vertices** (expensive) |
85 | | **Roughness Map** | Controls how **rough or smooth** a surface appears |
86 | | **Metalness Map** | Defines which parts behave like **metal** |
87 | | **AO (Ambient Occlusion)** | Simulates shadows in crevices for **realism** |
88 | | **Alpha Map** | Controls **transparency** |
89 | | **Emissive Map** | Makes parts of a model **glow** |
90 | | **Specular Map** | (older model) Defines **highlight strength** |
91 | | **Environment Map** | Simulates **reflections** (skybox, cubemap, etc.) |
92 |
93 | ### Normal Map vs Bump Map vs Height Map vs Displacement Map
94 |
95 | | Type | Alters Geometry? | Alters Lighting? | Affects Silhouette? | Realism | Performance |
96 | | -------------------- | ---------------- | ----------------- | ------------------- | --------------------------------- | ----------- |
97 | | **Bump Map** | ❌ No | ✅ Yes (simple) | ❌ No | Low | Fast |
98 | | **Normal Map** | ❌ No | ✅ Yes (accurate) | ❌ No | Medium | Medium |
99 | | **Height Map** | 🔸 Used as input | ❌ By itself | ❌ No | Used for Displacement or Parallax | |
100 | | **Displacement Map** | ✅ Yes | ✅ Yes (indirect) | ✅ Yes | High | Slowest |
101 |
102 | ### All Mesh Materials in Three.js
103 |
104 | 1. `MeshBasicMaterial`
105 |
106 | - Lighting: ❌ No lighting, no shadows.
107 | - Use: Simple unlit color, UI, wireframes, always fully bright.
108 | - Realism: ❌ None.
109 | - Performance: ✅ Very fast.
110 |
111 | 2. `MeshDepthMaterial`
112 |
113 | - Lighting: ❌ No direct lighting.
114 | - Use: Renders depth from camera — black = near, white = far.
115 | - Realism: ❌ Not for direct use, but useful for shadows, post-processing, fog.
116 | - Performance: ✅ Fast.
117 |
118 | 3. `MeshDistanceMaterial`
119 |
120 | - Lighting: ❌ No direct lighting.
121 | - Use: Encodes distance to a reference point/light (used in shadow computations like RectAreaLightShadow).
122 | - Realism: ❌ Not visible directly in scenes.
123 | - Performance: ✅ Fast.
124 |
125 | 4. `MeshLambertMaterial`
126 |
127 | - Lighting: ✅ Yes (Lambertian shading – diffuse only).
128 | - Use: Basic lit material. No specular highlights.
129 | - Realism: 🟠 Low-medium.
130 | - Performance: ✅ Fast.
131 | - Limitations: No specular (shiny) reflections.
132 |
133 | 5. `MeshMatcapMaterial`
134 |
135 | - Lighting: ❌ No real lighting. Uses a baked lighting image (matcap).
136 | - Use: Stylized rendering (e.g., sculpting preview, clay material).
137 | - Realism: 🟡 Depends on the matcap.
138 | - Performance: ✅ Very fast.
139 | - Note: Lighting baked into texture; rotates with camera.
140 |
141 | 6. `MeshNormalMaterial`
142 |
143 | - Lighting: ❌ No lights, shows surface normals.
144 | - Use: Debug surface orientation. Color = normal vector.
145 | - Realism: ❌ None.
146 | - Performance: ✅ Very fast.
147 |
148 | 7. `MeshPhongMaterial`
149 |
150 | - Lighting: ✅ Yes (Phong model – diffuse + specular).
151 | - Use: Glossy surfaces, shininess, metal, glass.
152 | - Realism: 🟡 Moderate.
153 | - Performance: 🟡 Medium.
154 | - Note: Legacy, not PBR (Physically Based Rendering). Still good for simple shiny objects.
155 |
156 | > **NOTE**
157 | >
158 | > What is physically based rendering?
159 | > Physically based rendering (PBR) is a method of shading in computer graphics that aims to simulate the physical behavior of a light beam and its interaction with materials to achieve photoreal visuals. It employs algorithms based on physically accurate formulas to replicate real-world materials, resulting in cohesive and photorealistic environments.
160 |
161 | 8. `MeshStandardMaterial`
162 |
163 | - Lighting: ✅ Full PBR (Physically Based Rendering).
164 | - Use: Real-world surfaces (wood, metal, stone, etc.).
165 | - Realism: ✅ High.
166 | - Performance: 🔵 Slower than Phong.
167 | - Supports: Roughness, metalness, environment maps, AO, etc.
168 |
169 | 9. `MeshPhysicalMaterial`
170 |
171 | - Lighting: ✅ Full PBR + extended physical features.
172 | - Use: Advanced materials like glass, clearcoat, fabric.
173 | - Realism: ✅✅ Highest.
174 | - Performance: 🔴 Heaviest (most computations).
175 | - Supports Extra: clearcoat, transmission, ior, sheen, etc.
176 |
177 | 10. `MeshToonMaterial`
178 |
179 | - Lighting: ✅ Yes, uses cel-shading style.
180 | - Use: Cartoon, stylized look. Requires gradientMap.
181 | - Realism: ❌ Stylized.
182 | - Performance: 🟢 Efficient.
183 | - Note: Great for anime/comic-style rendering.
184 |
185 | (^ `MeshToonMaterial` is my favourite one)
186 |
187 | ### Summary table
188 |
189 | | Material | Lighting | Shadows | Realism | Style | Performance | Use Case |
190 | | ---------------------- | ---------- | ------- | ----------- | ------------- | ----------- | ---------------------------- |
191 | | `MeshBasicMaterial` | ❌ No | ❌ No | ❌ None | Flat/Unlit | ✅ Fastest | UI, colors, always visible |
192 | | `MeshDepthMaterial` | ❌ No | N/A | ❌ None | Depth Debug | ✅ Fast | Shadow mapping, fog, effects |
193 | | `MeshDistanceMaterial` | ❌ No | N/A | ❌ None | Distance Calc | ✅ Fast | Shadow distance computation |
194 | | `MeshLambertMaterial` | ✅ Diffuse | ✅ Yes | 🟠 Basic | Matte | ✅ Fast | Simple lighting |
195 | | `MeshMatcapMaterial` | ❌ Baked | ❌ No | Stylized | Sculpting | ✅ Fast | Clay/anime/matcap style |
196 | | `MeshNormalMaterial` | ❌ No | ❌ No | ❌ None | Debug | ✅ Fast | Visualize normals |
197 | | `MeshPhongMaterial` | ✅ Yes | ✅ Yes | 🟡 Moderate | Glossy | 🟡 Medium | Old-school shiny objects |
198 | | `MeshStandardMaterial` | ✅ PBR | ✅ Yes | ✅ High | Realistic | 🔵 Slower | Modern realism |
199 | | `MeshPhysicalMaterial` | ✅ PBR++ | ✅ Yes | ✅✅ Ultra | Glass, fabric | 🔴 Slowest | Advanced realism |
200 | | `MeshToonMaterial` | ✅ Yes | ✅ Yes | ❌ Stylized | Cartoon | 🟢 Good | Toon/anime effects |
201 |
202 | ## The Two RGB Worlds: Linear vs. sRGB
203 |
204 | ### sRGB: Perceptual, Non‑Linear Space
205 |
206 | - **Designed for our eyes**: Humans are more sensitive to dark tones than bright ones, so sRGB applies a gamma curve that allocates more precision where we notice it most.
207 | - **Great for display**: Images and textures saved in sRGB “look right” on monitors, TVs, phones.
208 | - **Bad for math**: Because the relationship between stored values and actual intensity is non‑linear, blending or lighting computations on sRGB data produce incorrect results—shading that’s too bright or too dark.
209 |
210 | ### Linear: The Math‑Friendly Space
211 |
212 | - **True proportionality**: A value of 0.5 is exactly half the light of 1.0.
213 | - **Essential for lighting**: All physically based rendering (PBR) lighting, shadowing, and material math assume linear arithmetic to model energy conservation and realistic falloff.
214 |
215 | ---
216 |
217 | ## Mixing Spaces: The Root of Many Rendering Bugs
218 |
219 | Since textures, colors, and light intensities all start as simple RGB numbers, it’s easy to forget which space you’re in. Accidentally lighting in sRGB space leads to:
220 |
221 | - **Gamma artifacts**—unexpected banding, blow‑outs, or muddy shadows
222 | - **Inaccurate color shifts**—your bright highlights won’t “feel” physically correct
223 |
224 | ---
225 |
226 | ## Why HDR? Capturing Real‑World Brightness
227 |
228 | Standard (LDR) renders clamp values into [0…1], so a blazing sun and a candle flame both end up somewhere in that tiny range. HDR (High Dynamic Range) lets you:
229 |
230 | 1. **Use floating‑point buffers** (e.g. `RGBE`, `RGBA16F`) so intensities ost.
231 | 2. **Drive PBR shaders**—materials like `MeshStandardMaterial` or `MeshPhysicalMaterial` expect HDR input to simulate metallic reflections, bloom, etc.
232 | 3. **Preserve contrast**—from the deepest shadows (0.01) to the brightest speculars (1000.0+).
233 |
234 | ---
235 |
236 | ## Tone Mapping: From HDR to Your Monitor
237 |
238 | Monitors still only accept [0…1] in sRGB, so you need a “compressor”:
239 |
240 | - **Linear**: Straight scale of all values; often too flat or washed‑out.
241 | - **Reinhard**: A filmic classic:
242 | ```jsx
243 | result = color / (color + 1);
244 | ```
245 | → rolls off bright areas into soft white.
246 | - **Cineon**: Designed to mimic film scan response.
247 | - **ACES**: Academies’ standard: more gamut, more contrast, filmic but controlled.
248 | - **AcesNeutral (AgX/Neutral)**: A newer, subtler “balanced” look—minimal hue shifts, realistic preserves.
249 |
250 | Tone‑mapping operators each have trade‑offs in contrast, saturation, and highlight roll‑off; choosing one depends on your artistic needs.
251 |
252 | ---
253 |
254 | ## Putting It All Together in Three.js
255 |
256 | 1. **Textures & Color Spaces**
257 |
258 | - **Color textures** (diffus, emissive):
259 | ```jsx
260 | texture.colorSpace = THREE.SRGBColorSpace;
261 | ```
262 | - **Data textures** (normal, height, roughness, metalness):
263 | ```jsx
264 | texture.colorSpace = THREE.LinearColorSpace;
265 | ```
266 |
267 | Data maps do not represent “color” but raw values, so they must stay linear.
268 |
269 | 2. **Scene Colors**
270 |
271 | When you write `new THREE.Color(0xffeecc)`, assume sRGB. Three.js will convert to linear under-the-hood before lighting.
272 |
273 | 3. **Renderer Setup**
274 |
275 | ```jsx
276 | const renderer = new THREE.WebGLRenderer({ antialias: true });
277 | renderer.outputColorSpace = THREE.SRGBColorSpace;
278 | renderer.toneMapping = THREE.ACESFilmicToneMapping; // or Reinhard, Cineon, rtc.
279 | renderer.toneMappingExposure = 1.0; // tweak for scene brightnes
280 | ```
281 |
282 | 4. **Lighting Calculations**
283 |
284 | Three.js does all lighting in linear space automatically, so long as your inputs are tagged correctly.
285 |
286 | 5. **Custom Shaders**
287 |
288 | If you write GLSL:
289 |
290 | ```glsl
291 | vec3 linearColor = pow(texel.rgb, vec3(2.2)); // sRGB → linear
292 | // … lighting math in linear …
293 | vec3 finalSRGB = pow(linearColor, vec3(1.0/2.2)); // linear → sRGB
294 | gl_FragColor = vec4(finalSRGB, texel.a);
295 | ```
296 |
297 | ---
298 |
299 | ## Best Practices & Gotchas
300 |
301 | - **Always label your textures** with the correct `colorSpace`.
302 | - **Keep lighting math linear**—never blend or add raw sRGB values.
303 | - **Pick a tone‑mapper** early, then adjust exposures as you light your scene.
304 | - **Check your final look** on an sRGB‑calibrated display; HDR previews in some browsers/OSs can deceive you.
305 |
306 | ---
307 |
308 | By respecting the separation between perceptual (sRGB) and arithmetic (linear) spaces, leveraging HDR buffers, and choosing an appropriate tone‑mapping curve, you unlock true-to-life lighting from the soft glow of a candle to the blinding radiance of the sun.
309 |
--------------------------------------------------------------------------------