Skip to content

Commit 0b9f112

Browse files
committed
actual tracing
1 parent 18103b0 commit 0b9f112

File tree

3 files changed

+224
-36
lines changed

3 files changed

+224
-36
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ opt-level = 1
1111

1212
[dependencies]
1313
bytemuck = "1.24.0"
14+
glam = { version = "0.30.9", features = ["bytemuck"] }
1415
pollster = "0.4.0"
16+
rand = "0.9.2"
1517
wgpu = "24.0.1"
1618
winit = "0.29"

assets/compute.wgsl

Lines changed: 126 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
11
struct Params {
2+
camera_pos: vec3f,
3+
_pad1: u32,
24
width: u32,
35
height: u32,
46
iTime: f32,
7+
_pad2: u32,
8+
};
9+
10+
struct Material {
11+
diffuse_color: vec3f,
12+
_pad: f32,
13+
emission_color: vec3f,
14+
emission_strength: f32,
15+
};
16+
17+
struct Sphere {
18+
center: vec3f,
19+
radius: f32,
20+
material: Material,
521
};
622

723
@group(0) @binding(0) var<uniform> params: Params;
824
@group(0) @binding(1) var outputTex: texture_storage_2d<rgba8unorm, write>;
25+
@group(0) @binding(2) var<storage, read> spheres: array<Sphere>;
26+
27+
const PI: f32 = 3.141592;
28+
29+
const MAX_BOUNCES: u32 = 5u;
930

1031
struct Ray {
1132
origin: vec3f,
@@ -16,46 +37,133 @@ struct RayHit {
1637
distance: f32,
1738
position: vec3f,
1839
normal: vec3f,
40+
material: Material,
1941
hit: bool,
2042
};
2143

22-
fn sphere_intersect(ray: Ray, center: vec3f, radius: f32) -> RayHit {
23-
let oc = ray.origin - center;
44+
fn hash(seed: vec2f) -> u32 {
45+
var h = u32(seed.x * 73856093.0) ^ u32(seed.y * 19349663.0);
46+
h = (h ^ (h >> 16u)) * 0x45d9f3bu;
47+
h = (h ^ (h >> 16u)) * 0x45d9f3bu;
48+
h = h ^ (h >> 16u);
49+
return h;
50+
}
51+
52+
fn next_random(state: ptr<function, u32>) -> u32 {
53+
(*state) = (*state) * 1664525u + 1013904223u;
54+
let result = ((*state >> ((*state >> 28u) + 4u)) ^ *state) * 277803737u;
55+
return (result >> 22u) ^ result;
56+
}
57+
58+
fn random_value(state: ptr<function, u32>) -> f32 {
59+
return f32(next_random(state)) / 4294967295.0;
60+
}
61+
62+
fn random_normal_distribution(state: ptr<function, u32>) -> f32 {
63+
let theta = 2.0 * PI * random_value(state);
64+
let rho = sqrt(-2.0 * log(random_value(state)));
65+
return rho * cos(theta);
66+
}
67+
68+
fn random_direction(state: ptr<function, u32>) -> vec3f {
69+
let x = random_normal_distribution(state);
70+
let y = random_normal_distribution(state);
71+
let z = random_normal_distribution(state);
72+
return normalize(vec3f(x, y, z));
73+
}
74+
75+
fn random_hemisphere_dir(normal: vec3f, state: ptr<function, u32>) -> vec3f {
76+
let dir = random_direction(state);
77+
if dot(dir, normal) < 0.0 {
78+
return -dir;
79+
} else {
80+
return dir;
81+
}
82+
}
83+
84+
fn trace(ray: ptr<function, Ray>, state: ptr<function, u32>) -> vec3f {
85+
var light = vec3f(0.0, 0.0, 0.0);
86+
var color = vec3f(1.0, 1.0, 1.0);
87+
88+
for (var bounce: u32 = 0u; bounce < MAX_BOUNCES; bounce = bounce + 1u) {
89+
let hit = calculate_collision(*ray);
90+
if hit.hit {
91+
(*ray).origin = hit.position + hit.normal * 0.001;
92+
(*ray).direction = random_hemisphere_dir(hit.normal, state);
93+
94+
let emitted = hit.material.emission_color * hit.material.emission_strength;
95+
light = light + color * emitted;
96+
color = color * hit.material.diffuse_color;
97+
} else {
98+
break;
99+
}
100+
}
101+
102+
return light;
103+
}
104+
105+
fn calculate_collision(ray: Ray) -> RayHit {
106+
var closest_hit = RayHit(
107+
0.0,
108+
vec3f(0.0),
109+
vec3f(0.0),
110+
Material(vec3f(0.0), 0.0, vec3f(0.0), 0.0),
111+
false
112+
);
113+
for (var i: u32 = 0u; i < params._pad2; i = i + 1u) {
114+
let hit = sphere_intersect(ray, spheres[i]);
115+
if hit.hit && (!closest_hit.hit || hit.distance < closest_hit.distance) {
116+
closest_hit = hit;
117+
}
118+
}
119+
return closest_hit;
120+
}
121+
122+
fn sphere_intersect(ray: Ray, sphere: Sphere) -> RayHit {
123+
var hit = RayHit(
124+
0.0,
125+
vec3f(0.0),
126+
vec3f(0.0),
127+
Material(vec3f(0.0), 0.0, vec3f(0.0), 0.0),
128+
false
129+
);
130+
let oc = ray.origin - sphere.center;
24131
let a = dot(ray.direction, ray.direction);
25132
let b = 2.0 * dot(oc, ray.direction);
26-
let c = dot(oc, oc) - radius * radius;
133+
let c = dot(oc, oc) - sphere.radius * sphere.radius;
27134
let discriminant = b * b - 4.0 * a * c;
28135
if discriminant < 0.0 {
29-
return RayHit(0.0, vec3f(0.0), vec3f(0.0), false);
136+
return hit;
30137
} else {
31138
let t = (-b - sqrt(discriminant)) / (2.0 * a);
32139
if t > 0.0 {
33-
let position = ray.origin + t * ray.direction;
34-
let normal = normalize(position - center);
35-
return RayHit(t, position, normal, true);
140+
hit.distance = t;
141+
hit.material = sphere.material;
142+
hit.position = ray.origin + t * ray.direction;
143+
hit.normal = normalize(hit.position - sphere.center);
144+
hit.hit = true;
145+
return hit;
36146
} else {
37-
return RayHit(0.0, vec3f(0.0), vec3f(0.0), false);
147+
return hit;
38148
}
39149
}
40150
}
41151

42-
const PI: f32 = 3.14159265;
43-
44152
@compute @workgroup_size(16, 16)
45153
fn main(@builtin(global_invocation_id) global_ix: vec3<u32>) {
46-
let fragCoord = vec2f(global_ix.xy) / vec2f(f32(params.width), f32(params.height)) - vec2f(0.5, 0.5);
154+
let fragCoord = vec2f(global_ix.xy) / vec2f(f32(params.width), f32(params.height)) ;
47155

48-
let camera_pos = vec3f(0.0, 0.0, 5.0);
49156
let aspect_ratio = f32(params.width) / f32(params.height);
50-
let fov = 60.0 * PI / 180.0;
51-
let px = (2.0 * (fragCoord.x + 0.5) - 1.0) * tan(fov / 2.0) * aspect_ratio;
52-
let py = (1.0 - 2.0 * (fragCoord.y + 0.5)) * tan(fov / 2.0);
157+
let half_fov_tan = tan(radians(60.0) * 0.5);
158+
let px = (2.0 * fragCoord.x - 1.0) * half_fov_tan * aspect_ratio;
159+
let py = (1.0 - 2.0 * fragCoord.y) * half_fov_tan;
53160
let ray_dir = normalize(vec3f(px, py, -1.0));
54-
let ray = Ray(camera_pos, ray_dir);
161+
var ray = Ray(params.camera_pos, ray_dir);
162+
163+
var state = hash(fragCoord.xy + params.iTime);
55164

56-
let hit = f32(u32(sphere_intersect(ray, vec3f(0.0), 1.0).hit));
57165

58-
let fragColor = vec4f(hit, hit, hit, 1.0);
166+
let fragColor = vec4f(trace(&ray, &state), 1.0);
59167

60168
textureStore(outputTex, vec2i(global_ix.xy), fragColor);
61169
}

0 commit comments

Comments
 (0)