Skip to content

Commit 4a76335

Browse files
committed
Implement cosine-weighted hemisphere sampling
1 parent c83b83e commit 4a76335

File tree

2 files changed

+19
-16
lines changed

2 files changed

+19
-16
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ An experimental pathtracer, implemented using WebGPU via the [Dawn](https://dawn
88
- Great tool for checking correct memory layout
99
- _Physically Based Rendering, fourth edition_
1010
- Bounding volume hierarchy
11+
- Cosine-weighted hemisphere sampling method
1112
- [scratchapixel/moller-trumbore-ray-triangle-intersection](https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-rendering-a-triangle/moller-trumbore-ray-triangle-intersection.html)
1213
- _The Reference Path Tracer, Ray Tracing Gems II_ and associated code sample [boksajak/referencePT](https://github.com/boksajak/referencePT/)
1314
- Rng initialization

src/pt/raytracer.wgsl

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ struct TriangleHit {
124124
t: f32,
125125
}
126126

127+
struct Scatter {
128+
wi: vec3f,
129+
throughput: vec3f,
130+
}
131+
127132
fn rayColor(primaryRay: Ray, rngState: ptr<function, u32>) -> vec3f {
128133
var ray = primaryRay;
129134

@@ -133,10 +138,9 @@ fn rayColor(primaryRay: Ray, rngState: ptr<function, u32>) -> vec3f {
133138
var intersection: Intersection;
134139
if rayIntersectBvh(ray, T_MAX, &intersection) {
135140
let p = intersection.p;
136-
let scatterDirection = sampleLambertian(intersection, rngState);
137-
let scatterThroughput = UNIFORM_HEMISPHERE_MULTIPLIER * evalLambertian(intersection, scatterDirection);
138-
ray = Ray(p, scatterDirection);
139-
throughput *= scatterThroughput;
141+
let scatter = evalImplicitLambertian(intersection, rngState);
142+
ray = Ray(p, scatter.wi);
143+
throughput *= scatter.throughput;
140144
} else {
141145
let unitDirection = normalize(ray.direction);
142146
let t = 0.5f * (unitDirection.y + 1f);
@@ -156,17 +160,16 @@ fn generateCameraRay(camera: Camera, rngState: ptr<function, u32>, u: f32, v: f3
156160
return Ray(origin, direction);
157161
}
158162

159-
fn sampleLambertian(hit: Intersection, rngState: ptr<function, u32>) -> vec3f {
160-
let v = rngNextInUnitHemisphere(rngState);
163+
fn evalImplicitLambertian(hit: Intersection, rngState: ptr<function, u32>) -> Scatter {
164+
let v = rngNextInCosineWeightedHemisphere(rngState);
161165
let onb = pixarOnb(hit.n);
162-
return onb * v;
163-
}
166+
let wi = onb * v;
164167

165-
fn evalLambertian(hit: Intersection, wi: vec3f) -> vec3f {
166168
let textureDesc = textureDescriptors[textureDescriptorIndices[hit.triangleIdx]];
167169
let uv = hit.uv;
168170
let albedo = textureLookup(textureDesc, uv);
169-
return albedo * FRAC_1_PI * max(EPSILON, dot(hit.n, wi));
171+
172+
return Scatter(wi, albedo);
170173
}
171174

172175
fn pixarOnb(n: vec3f) -> mat3x3f {
@@ -352,16 +355,15 @@ fn textureLookup(desc: TextureDescriptor, uv: vec2f) -> vec3f {
352355
}
353356

354357
@must_use
355-
fn rngNextInUnitHemisphere(state: ptr<function, u32>) -> vec3f {
358+
fn rngNextInCosineWeightedHemisphere(state: ptr<function, u32>) -> vec3f {
356359
let r1 = rngNextFloat(state);
357360
let r2 = rngNextFloat(state);
361+
let sqrtR2 = sqrt(r2);
358362

363+
let z = sqrt(1f - r2);
359364
let phi = 2f * PI * r1;
360-
let sinTheta = sqrt(1f - r2 * r2);
361-
362-
let x = cos(phi) * sinTheta;
363-
let y = sin(phi) * sinTheta;
364-
let z = r2;
365+
let x = cos(phi) * sqrtR2;
366+
let y = sin(phi) * sqrtR2;
365367

366368
return vec3(x, y, z);
367369
}

0 commit comments

Comments
 (0)