Skip to content

Commit cf98b24

Browse files
committed
Added raytracing and shading
1 parent 69e5331 commit cf98b24

10 files changed

Lines changed: 237 additions & 64 deletions

File tree

src/math/intersection.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import type { Mesh } from "../rendering/types/mesh";
2+
import { mul_mat4_vec4 } from "./matrix_operators";
3+
import { vec4, type Line, type mat4, type vec3 } from "./types";
4+
5+
export function ray_intersects_ellipsoid(
6+
line:Line,
7+
inverse_model:mat4,
8+
radius_reciprocal:vec3
9+
): boolean{
10+
if(!inverse_model) return false;
11+
12+
const local_origin = mul_mat4_vec4(inverse_model,vec4(line.point[0],line.point[1],line.point[2],1.0));
13+
const local_dir = mul_mat4_vec4(inverse_model,vec4(line.directional_vector[0],line.directional_vector[1],line.directional_vector[2],0.0));
14+
15+
16+
const [ux,uy,uz,uu] = local_dir;
17+
const px = local_origin[0];
18+
const py = local_origin[1]
19+
const pz = local_origin[2]
20+
21+
const [recip_a,recip_b,recip_c] = radius_reciprocal;
22+
const A = ux * ux * recip_a + uy * uy * recip_b + uz * uz * recip_c;
23+
const B = 2 * (px * ux * recip_a + py * uy * recip_b + pz * uz * recip_c);
24+
const C = px * px * recip_a + py * py * recip_b + pz * pz * recip_c - 1;
25+
26+
if(C<=0) return true;
27+
28+
if(B>0) return false;
29+
30+
const delta = B*B - 4 * A * C;
31+
return delta >= 0;
32+
}
33+
34+
export function ray_intersects_triangles(
35+
ray: Line,
36+
mesh: Mesh,
37+
inverse_model: mat4
38+
): boolean {
39+
const local_origin = mul_mat4_vec4(inverse_model, vec4(ray.point[0], ray.point[1], ray.point[2], 1.0));
40+
const local_dir = mul_mat4_vec4(inverse_model, vec4(ray.directional_vector[0], ray.directional_vector[1], ray.directional_vector[2], 0.0));
41+
42+
const ox = local_origin[0];
43+
const oy = local_origin[1];
44+
const oz = local_origin[2];
45+
46+
const dx = local_dir[0];
47+
const dy = local_dir[1];
48+
const dz = local_dir[2];
49+
50+
const vertices = mesh.vertices;
51+
const indices = mesh.indices;
52+
53+
const EPSILON = 0.00001;
54+
55+
for (let i = 0; i < indices.length; i += 3) {
56+
const i0 = indices[i] * 3;
57+
const i1 = indices[i + 1] * 3;
58+
const i2 = indices[i + 2] * 3;
59+
60+
const v0x = vertices[i0];
61+
const v0y = vertices[i0 + 1];
62+
const v0z = vertices[i0 + 2];
63+
64+
const v1x = vertices[i1];
65+
const v1y = vertices[i1 + 1];
66+
const v1z = vertices[i1 + 2];
67+
68+
const v2x = vertices[i2];
69+
const v2y = vertices[i2 + 1];
70+
const v2z = vertices[i2 + 2];
71+
72+
const e1x = v1x - v0x;
73+
const e1y = v1y - v0y;
74+
const e1z = v1z - v0z;
75+
76+
const e2x = v2x - v0x;
77+
const e2y = v2y - v0y;
78+
const e2z = v2z - v0z;
79+
80+
const hx = dy * e2z - dz * e2y;
81+
const hy = dz * e2x - dx * e2z;
82+
const hz = dx * e2y - dy * e2x;
83+
84+
const a = e1x * hx + e1y * hy + e1z * hz;
85+
86+
if (a > -EPSILON && a < EPSILON) {
87+
continue;
88+
}
89+
90+
const f = 1.0 / a;
91+
92+
const sx = ox - v0x;
93+
const sy = oy - v0y;
94+
const sz = oz - v0z;
95+
96+
const u = f * (sx * hx + sy * hy + sz * hz);
97+
if (u < 0.0 || u > 1.0) {
98+
continue;
99+
}
100+
101+
const qx = sy * e1z - sz * e1y;
102+
const qy = sz * e1x - sx * e1z;
103+
const qz = sx * e1y - sy * e1x;
104+
const v = f * (dx * qx + dy * qy + dz * qz);
105+
if (v < 0.0 || u + v > 1.0) {
106+
continue;
107+
}
108+
const t = f * (e2x * qx + e2y * qy + e2z * qz);
109+
110+
if (t > EPSILON && t < 1.0) {
111+
return true;
112+
}
113+
}
114+
115+
return false;
116+
}

src/rendering/process_lighting.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import { ray_intersects_ellipsoid, ray_intersects_triangles } from "../math/intersection";
12
import { dot_vec3, length_vec3, mul_mat4_vec4, mul_mat4_vec4_mut, sub_vec3 } from "../math/matrix_operators";
2-
import { ArrayType, mat4, vec3, vec4 } from "../math/types";
3+
import { ArrayType, mat4, vec3, vec4, type Line } from "../math/types";
34
import type { Light } from "./types/light";
45
import type { Mesh } from "./types/mesh";
56
import type { Scene } from "./types/scene";
67

78

8-
export function process_lighting(mesh:Mesh, scene:Scene, camera_coords:vec3 | null = null){
9+
export function process_lighting(mesh:Mesh, scene:Scene, inverse_models:mat4[], camera_coords:vec3 | null = null){
910
const world_coordinates = mesh.projected_buffer;
1011
const lights = scene.lights;
1112
let vertex:vec3 = vec3(0,0,0);
@@ -26,14 +27,43 @@ export function process_lighting(mesh:Mesh, scene:Scene, camera_coords:vec3 | nu
2627

2728
let color = vec3(0,0,0);
2829
for(const light of lights){
30+
let in_shadow:boolean = false;
2931
const L = sub_vec3(light.position,vertex);
3032
const dist = length_vec3(L) || k;
31-
L[0]/=dist;
32-
L[1]/=dist;
33-
L[2]/=dist;
3433
if (dist > light.radius){
3534
continue;
3635
}
36+
37+
38+
if(light.casts_shadow){
39+
const ray:Line = {
40+
point:vertex,
41+
directional_vector: L
42+
};
43+
for(let j = 0; j < scene.meshes.length; ++j){
44+
const target_mesh = scene.meshes[j];
45+
if(target_mesh.transparent){
46+
continue;
47+
}
48+
const target_inverse = inverse_models[j];
49+
50+
if(ray_intersects_ellipsoid(ray,target_inverse,target_mesh.radius_reciprocal)){
51+
if(ray_intersects_triangles(ray,target_mesh,target_inverse)){
52+
in_shadow = true;
53+
break;
54+
}
55+
}
56+
}
57+
}
58+
59+
if(in_shadow){
60+
continue;
61+
}
62+
63+
L[0]/=dist;
64+
L[1]/=dist;
65+
L[2]/=dist;
66+
3767
const dot = L[0] * nx + L[1] * ny + L[2] * nz;
3868
const diffuse = Math.max(0,dot);
3969
const window = 1//*Math.pow(Math.max(0, 1 - Math.pow(dist / light.radius, 4)), 2)
@@ -80,7 +110,7 @@ export function process_lighting(mesh:Mesh, scene:Scene, camera_coords:vec3 | nu
80110
* @param camera_coord The coordinate of the Camera (pass this if you want a functioning reflection model)
81111
*/
82112

83-
export function process_world_coordinates(scene:Scene, model:mat4[], camera_coord:vec3 | null = null){
113+
export function process_world_coordinates(scene:Scene, model:mat4[], inverse_model:mat4[], camera_coord:vec3 | null = null){
84114
let v:vec4 = vec4(0,0,0,0);
85115
let mesh_index = 0;
86116
for(const mesh of scene.meshes){
@@ -98,6 +128,6 @@ export function process_world_coordinates(scene:Scene, model:mat4[], camera_coor
98128
mesh.projected_buffer[out_index++] = v[2];
99129
mesh.projected_buffer[out_index++] = v[3];
100130
}
101-
process_lighting(mesh,scene,camera_coord);
131+
process_lighting(mesh,scene, inverse_model, camera_coord);
102132
}
103133
}

src/rendering/types/geometry.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import { ArrayType, IndexingType } from "../../math/types";
1+
import { ArrayType, IndexingType, vec3 } from "../../math/types";
22

33

44
export class Geometry{
55
vertices:ArrayType;
66
indices:IndexingType;
77
normals:ArrayType;
8+
9+
radius:vec3 = vec3(1,1,1);
10+
radius_reciprocal:vec3 = vec3(1,1,1);
11+
812
constructor(vertices:ArrayType, indices:IndexingType, normals:ArrayType | null = null){
913
this.vertices = vertices;
1014
this.indices = indices;
@@ -14,6 +18,7 @@ export class Geometry{
1418
this.normals = new ArrayType(this.vertices.length);
1519
this.compute_normals();
1620
}
21+
this.determine_radius();
1722
}
1823
private compute_normals() {
1924
const inds = this.indices;
@@ -50,4 +55,19 @@ export class Geometry{
5055
}
5156
}
5257
}
58+
59+
private determine_radius(){
60+
const EPSILON = 0.0001;
61+
const biggest:vec3 = vec3(EPSILON,EPSILON,EPSILON);
62+
const vertices = this.vertices;
63+
for(let i = 0 ; i < vertices.length; i+=3){
64+
for(let j = 0; j < 3; j++){
65+
biggest[j] = Math.max(biggest[j],Math.abs(vertices[i+j]));
66+
}
67+
}
68+
this.radius = biggest;
69+
for(let j = 0; j < 3; ++j){
70+
this.radius_reciprocal[j] = 1/(this.radius[j] ** 2);
71+
}
72+
}
5373
}

src/rendering/types/light.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
1-
import type { vec3 } from "../../math/types";
1+
import type { ArrayType, mat4, vec3 } from "../../math/types";
22

33
export class Light{
44
position:vec3;
55
color:vec3;
66
intensity:number;
77
radius:number;
88

9+
casts_shadow:boolean = false;
10+
911
direction?: vec3;
1012
cutoff?: number;
1113

12-
constructor(position:vec3, color:vec3, intensity:number, radius:number, direction?:vec3, cutoff_angle?:number){
14+
constructor(position:vec3, color:vec3, intensity:number, radius:number, casts_shadow:boolean = false, direction?:vec3, cutoff_angle?:number){
1315
this.position = position;
1416
this.color = color;
1517
this.intensity = intensity;
1618
this.radius = radius;
19+
this.casts_shadow = casts_shadow;
1720
if(direction)
1821
this.direction = direction;
1922
if(cutoff_angle)

src/rendering/types/mesh.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ export class Mesh{
1313
raster_color: ArrayType;
1414
normals: ArrayType;
1515
albedo: vec3;
16+
17+
transparent:boolean = false;
18+
19+
radius:vec3;
20+
radius_reciprocal:vec3;
21+
1622
specular_coefficient: number;
1723
raster_end:number;
1824
visible_triangles_count:number;
@@ -24,6 +30,10 @@ export class Mesh{
2430
this.local_normals = geometry.normals;
2531
this.specular_coefficient = specular_coefficient;
2632
this.normals = new ArrayType(geometry.indices.length*3);
33+
34+
this.radius = geometry.radius;
35+
this.radius_reciprocal = geometry.radius_reciprocal;
36+
2737
if(projected_buffer === null)
2838
this.projected_buffer = new ArrayType(this.vertices.length * 4 / 3);
2939
else

src/rendering/types/node.ts

Lines changed: 6 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { identity, translate, rotate, scale } from "../../math/transformations";
33
import type { Mesh } from "./mesh";
44
import { inverse_mat4, mul_mat4_vec4 } from "../../math/matrix_operators";
55
import type { Light } from "./light";
6+
import { ray_intersects_ellipsoid } from "../../math/intersection";
67

78
export class Node {
89
mesh: Mesh;
@@ -20,54 +21,20 @@ export class Node {
2021
constructor(mesh: Mesh, light:Light | null = null) {
2122
this.mesh = mesh;
2223
this.light = light;
24+
25+
this.radius = this.mesh.radius;
26+
this.radius_reciprocal = this.mesh.radius_reciprocal;
27+
2328
if(this.light){
2429
this.light.position = this.position;
2530
}
2631
this.update_matrix();
27-
this.determine_radius();
2832
}
2933

30-
private determine_radius(){
31-
const EPSILON = 0.0001;
32-
const biggest:vec3 = vec3(EPSILON,EPSILON,EPSILON);
33-
const vertices = this.mesh.vertices;
34-
for(let i = 0 ; i < vertices.length; i+=3){
35-
for(let j = 0; j < 3; j++){
36-
biggest[j] = Math.max(biggest[j],Math.abs(vertices[i+j]));
37-
}
38-
}
39-
this.radius = biggest;
40-
for(let j = 0; j < 3; ++j){
41-
this.radius_reciprocal[j] = 1/(this.radius[j] ** 2);
42-
}
43-
}
4434

4535

4636
intersects_with(line:Line){
47-
if(!this.inverse_model) return false;
48-
49-
50-
51-
const local_origin = mul_mat4_vec4(this.inverse_model,vec4(line.point[0],line.point[1],line.point[2],1.0));
52-
const local_dir = mul_mat4_vec4(this.inverse_model,vec4(line.directional_vector[0],line.directional_vector[1],line.directional_vector[2],0.0));
53-
54-
55-
const [ux,uy,uz,uu] = local_dir;
56-
const px = local_origin[0];
57-
const py = local_origin[1]
58-
const pz = local_origin[2]
59-
60-
const [recip_a,recip_b,recip_c] = this.radius_reciprocal;
61-
const A = ux * ux * recip_a + uy * uy * recip_b + uz * uz * recip_c;
62-
const B = 2 * (px * ux * recip_a + py * uy * recip_b + pz * uz * recip_c);
63-
const C = px * px * recip_a + py * py * recip_b + pz * pz * recip_c - 1;
64-
65-
if(C<=0) return true;
66-
67-
if(B>0) return false;
68-
69-
const delta = B*B - 4 * A * C;
70-
return delta >= 0;
37+
return ray_intersects_ellipsoid(line, this.inverse_model, this.radius_reciprocal);
7138
}
7239

7340

src/rendering/types/scene.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class Scene{
7171
this.lights = [];
7272
}
7373

74-
add_mesh(geometry:Geometry, color:vec3 = vec3(0.2,0.2,0.2), specular_coefficient:number = Math.random()):Mesh{
74+
add_mesh(geometry:Geometry, color:vec3 = vec3(0.2,0.2,0.2), transparent:boolean = false, specular_coefficient:number = Math.random()):Mesh{
7575
const scene_size = geometry.indices.length * 4;
7676
const proj_size = geometry.vertices.length/3*4;
7777
const color_size = geometry.vertices.length;
@@ -88,6 +88,7 @@ export class Scene{
8888
this.raster_color_cursor += raster_color_size;
8989

9090
const mesh = new Mesh(geometry,color,raster_color,scene_view,proj_view,color_view,specular_coefficient);
91+
mesh.transparent = transparent;
9192
this.meshes.push(mesh);
9293
return mesh;
9394
}

0 commit comments

Comments
 (0)