Skip to content

Commit 795addc

Browse files
committed
Adding lighting
1 parent df1a013 commit 795addc

18 files changed

Lines changed: 257 additions & 91 deletions

devlog.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,21 @@
1-
We got some new optimizations, one of which provides a visual effect. Also, news!
1+
We are now doing things with `path` and `fill` (and properly depth sorting)
22

3-
First of all, getting the purely optimizations things out of the way: we're stepping out of manually manipulating strings. I noticed that in my old benchmark, 8.2% of the time was being used in `build_3d_svg`. At first i found this to be weird, since this function is purely string manipulation, but, as it turns out, this is exactly the issue. String reallocation is expensive, and since they aren't mutable in Javascript, I can't just edit a pre-existing string.
4-
The solution to this is representing the strings as what they should be, character arrays. This involved the creation of a new helper class, StringBuffer, that represents strings as an uInt8 typed array, and the necessary helper functions to convert strings and numbers into this byte array method.
3+
Since we couldn't draw anything that wasn't either a black silhuette or a wireframe outline, the order in which things are drawn never really mattered. Now, it does.
4+
In the previous devlog, I "fixed" this by sorting triangles in such a way that what is _further_ from the screen is drawn first, and what is closer, last. This way, if everything is behind this object, it will be covered by it. It was to my horror, however, that when I went to try rendering the two rotating spheres, that this _did not work, at all._
55

6-
Another thing: I thought I wouldn't be able to use the `stroke` and `fill` attributes of svg because i thought this project could also be submitted to the #flavorless challenge. As it turns out, it isn't. So I get this freedom which, in turn, means I'll get to actually _color_ my 3D meshes.
7-
If you've done 3D rendering in OpenGL before, you'll likely be familiar with the GL_DEPTH_TESTING that you enable so two meshes in different z positions don't merge together. This is a nice bonus that comes pre-made, but, as with everything in our project, we had to implement ourselves through a technique called Painter's Algorithm. Usually, this is done through a technique called z-buffering, but this doesn't work for our svg based project, because it requires you to have pixels (while the only thing we have here are vertices).
6+
This was due to me sorting the triangles in each mesh individually rather than as global "scene" context. In simple terms:
7+
- What we were doing: `[3,1],[5,0] => [1,3],[0,5] => [1,3,0,5]`
8+
- What we were _supposed_ to do: `[3,1],[5,0] => [3,1,5,0] => [0,1,3,5]`
89

9-
And, one last optimization: backface culling. We simply don't draw vertices facing away from the camera.
10+
At a high level, this is an obvious enough fix: just merge all meshes into one huge array, and sort it. But it comes with a huge problem (for a memory nut, like me): we'll be _doubling_ the memory we allocate.
11+
Thankfully, Javascript kindly provides us with the `subarray` method, that gives us a "view", or a reference, to a memory chunk of a larger array. Consequentially, we can create a large buffer with all the data we'll need, and give each mesh a chunk of it.
12+
In our program, we call that large buffer a `Scene`.
13+
14+
After doing all of this, I went to render the two overlapping spheres and... it didn't work? I quickly learned that this was due to the way that `<path>` works in SVG: if we want depth, we need different DOM objects, rather than a single, large one. It is bad for performance, but ultimately necessary.
15+
16+
With all this, we got our vertex engine done! Attached, lots of triangles!
1017

1118
**Commits**
12-
[Commit f10e698](https://url.jam06452.uk/u0uj8u): Optimized string writing to use fixed-size buffer
13-
[Commit 07573cd](https://url.jam06452.uk/103qgwe): Depth sorting & Backface culling
19+
[Commit eaaa1ea](https://url.jam06452.uk/1r26kdk): Created Scene object
20+
[Commit 06e157a](https://url.jam06452.uk/vnhdm7): Finished Scene logic
21+
[Commit df1a013](https://url.jam06452.uk/bygzl): Depth working!

index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
viewBox="-1 -1 2 2"
1818
preserveAspectRatio="none"
1919
stroke-width ="0.01"
20-
id="container">
20+
id="container"
21+
shape-rendering ="optimizeSpeed"
22+
>
2123
<path id="render-target" fill="none" stroke="black" stroke-width="0.005" stroke-linejoin="round" />
2224
</svg>
2325
</td>

src/main.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { mat4, vec3 } from "./math/types";
22
import { perspective, identity, look_at, rotate, translate } from "./math/transformations";
3-
import { create_sphere } from "./rendering/primitives";
3+
import { create_sphere } from "./rendering/utils/primitives";
44
import { build_mesh, render_scene } from "./rendering/render";
55
import { StringBuffer } from "./utils/string_buffer";
6-
import { Mesh } from "./rendering/mesh";
7-
import { Scene } from "./rendering/scene";
6+
import { Mesh } from "./rendering/types/mesh";
7+
import { Scene } from "./rendering/types/scene";
88
import { mul_mat4 } from "./math/matrix_operators";
99
import { build_scene } from "./to_html";
1010

@@ -14,18 +14,19 @@ export function main_3d() {
1414
if (!target) return;
1515
const wireframe_el = document.getElementById('wireframe-mode') as HTMLInputElement;
1616
const do_wireframe:boolean = wireframe_el?.checked;
17-
const sun_mesh = create_sphere(1.5, 10, 10);
18-
const planet_mesh = create_sphere(0.5, 10, 10);
17+
const sun_mesh = create_sphere(1.5, 32, 32);
18+
const planet_mesh = create_sphere(0.5, 16, 16);
1919

2020
const scene:Scene = new Scene([sun_mesh.vertices,planet_mesh.vertices],[sun_mesh.indices,planet_mesh.indices]);
2121

22+
console.log(scene.draw_end)
2223

2324

2425
const y = vec3(0,1,0);
2526
const view:mat4 = look_at(vec3(0,2,6.5),vec3(0,0,0),y);
2627
const projection = perspective(60 * Math.PI / 180, 400/300, 0.1, 100);
2728
let time = 0;
28-
const string_buffer = new StringBuffer(1024*1024);
29+
const string_buffer = new StringBuffer(scene.scene_buffer.length * 50);
2930
const vp = mul_mat4(projection,view);
3031
const loop = () => {
3132
time += 0.01;

src/math/matrix_operators.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,17 @@ export function mul_mat4_vec4(A: mat4, u: vec4): vec4 {
100100
);
101101
}
102102

103+
export function mul_mat4_vec4_mut(A:mat4, u:vec4):void{
104+
const a = A[0] * u[0] + A[4] * u[1] + A[8] * u[2] + A[12] * u[3];
105+
const b = A[1] * u[0] + A[5] * u[1] + A[9] * u[2] + A[13] * u[3];
106+
const c = A[2] * u[0] + A[6] * u[1] + A[10] * u[2] + A[14] * u[3];
107+
const d = A[3] * u[0] + A[7] * u[1] + A[11] * u[2] + A[15] * u[3];
108+
u[0] = a;
109+
u[1] = b;
110+
u[2] = c;
111+
u[3] = d;
112+
}
113+
103114
export function invert_mat2(A: mat2): mat2 | null {
104115
const a0 = A[0], a1 = A[1], a2 = A[2], a3 = A[3];
105116
let det = a0 * a3 - a2 * a1;
@@ -211,6 +222,7 @@ export function add_vec3(u:vec3, v:vec3): vec3{
211222
return vec3(u[0]+v[0],u[1]+v[1],u[2]+v[2]);
212223
}
213224

225+
214226
export function add_mat(u:ArrayType, v:ArrayType):ArrayType{
215227
const out:ArrayType = new ArrayType(u.length);
216228
for(let i = 0; i < u.length; ++i){
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { mul_mat4_vec4, scalar_mult_vec3 } from "../math/matrix_operators";
2-
import { mat4, vec4, vec3, EPSILON, vec2 } from "../math/types";
3-
import type { Mesh } from "./mesh";
1+
import { mul_mat4_vec4, scalar_mult_vec3 } from "../../math/matrix_operators";
2+
import { mat4, vec4, vec3, EPSILON, vec2 } from "../../math/types";
3+
import type { Mesh } from "../types/mesh";
44

55
export function transform_vertex(mvp:mat4, vertex:vec3):vec4{
66
return mul_mat4_vec4(mvp,vertex);

src/rendering/depth_sorting.ts

Whitespace-only changes.

src/rendering/mesh.ts

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/rendering/process_lighting.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { length_vec3, mul_mat4_vec4, mul_mat4_vec4_mut } from "../math/matrix_operators";
2+
import { ArrayType, mat4, vec3, vec4 } from "../math/types";
3+
import type { Light } from "./types/light";
4+
import type { Mesh } from "./types/mesh";
5+
import type { Scene } from "./types/scene";
6+
7+
8+
export function process_lighting(mesh:Mesh, scene:Scene){
9+
const world_coordinates = mesh.projected_buffer;
10+
const lights = scene.lights;
11+
let vertex:vec3 = vec3(0,0,0);
12+
for(let i = 0; i < world_coordinates.length; i+=4){
13+
vertex[0] = world_coordinates[i];
14+
vertex[1] = world_coordinates[i+1];
15+
vertex[2] = world_coordinates[i+2];
16+
let color = vec3(0,0,0);
17+
let intensity = 0;
18+
for(const light of lights){
19+
vertex[0] -= light.position[0];
20+
vertex[1] -= light.position[1];
21+
vertex[2] -= light.position[2];
22+
const dist = length_vec3(vertex);
23+
24+
}
25+
}
26+
}
27+
28+
export function process_world_coordinates(scene:Scene, model:mat4[]){
29+
let v:vec4 = vec4(0,0,0,0);
30+
let mesh_index = 0;
31+
for(const mesh of scene.meshes){
32+
const mesh_model = model[mesh_index++];
33+
const vertices = mesh.vertices;
34+
for(let i = 0; i < vertices.length; i+=4){
35+
v[0] = vertices[i];
36+
v[1] = vertices[i+1];
37+
v[2] = vertices[i+2];
38+
v[3] = vertices[i+3];
39+
mul_mat4_vec4_mut(mesh_model,v);
40+
mesh.projected_buffer[i] = v[0];
41+
mesh.projected_buffer[i+1] = v[1];
42+
mesh.projected_buffer[i+2] = v[2];
43+
mesh.projected_buffer[i+3] = v[3];
44+
}
45+
process_lighting(mesh,scene);
46+
}
47+
}

src/rendering/render.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import { rasterize } from "./rasterizer";
1+
import { rasterize } from "./screen_space/rasterizer";
22
import { build_3d_svg } from "../to_html";
3-
import type { Mesh } from "./mesh";
3+
import type { Mesh } from "./types/mesh";
44
import { mul_mat4 } from "../math/matrix_operators";
55
import { ArrayType, type mat4 } from "../math/types";
6-
import { transform_vertices } from "./vertex";
7-
import { assemble_primitives } from "./primitive_assembler";
6+
import { transform_vertices } from "./clip_space/vertex";
7+
import { assemble_primitives } from "./screen_space/primitive_assembler";
88
import type { StringBuffer } from "../utils/string_buffer";
9-
import type { Scene } from "./scene";
9+
import type { Scene } from "./types/scene";
1010

1111
export function render(mesh: Mesh, model:mat4, view:mat4, projection:mat4, invert_y:boolean = true, stride:number = 4):void {
1212
const mvp = mul_mat4(mul_mat4(projection,view),model);

src/rendering/scene.ts

Lines changed: 0 additions & 36 deletions
This file was deleted.

0 commit comments

Comments
 (0)