Skip to content
This repository was archived by the owner on Nov 22, 2022. It is now read-only.

Commit 7c875ca

Browse files
author
Lucas Crane
authored
Next event estimation (#71)
* test next event estimation * start implementing shadow catcher * add shadow catcher support (kindof) * big cleanup * more tweaks * revert scene changes * set strataCount back to default
1 parent dc4c249 commit 7c875ca

File tree

10 files changed

+183
-207
lines changed

10 files changed

+183
-207
lines changed

scenes/renderer-test/main.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ function init() {
197197
const geo = new THREE.PlaneBufferGeometry(1000, 1000);
198198
const mat = new THREE.MeshStandardMaterial();
199199
mat.shadowCatcher = true;
200+
mat.roughness = 0.5;
201+
mat.metalness = 0.0;
200202
const mesh = new THREE.Mesh(geo, mat);
201203
mesh.rotateX(Math.PI / 2);
202204
model.add(mesh);

scenes/sample-models/main.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ function createGroundMesh() {
105105
const geo = new THREE.PlaneBufferGeometry(100, 100);
106106
const mat = new THREE.MeshStandardMaterial();
107107
mat.color.set(0xffffff);
108-
mat.roughness = 1.0;
108+
mat.roughness = 0.5;
109109
mat.metalness = 0.0;
110110
mat.shadowCatcher = true;
111111
const mesh = new THREE.Mesh(geo, mat);

src/renderer/RayTracePass.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ export function makeRayTracePass(gl, {
2121
const samplingDimensions = [];
2222

2323
for (let i = 1; i <= bounces; i++) {
24-
// specular or diffuse reflection, light importance sampling, material sampling, next path direction
25-
samplingDimensions.push(2, 2, 2, 2);
24+
// specular or diffuse reflection, light importance sampling, next path direction
25+
samplingDimensions.push(2, 2, 2);
2626
if (i >= 2) {
2727
// russian roulette sampling
2828
// this step is skipped on the first bounce

src/renderer/RenderingPipeline.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export function makeRenderingPipeline({
3030
// higher number results in faster convergence over time, but with lower quality initial samples
3131
const strataCount = 6;
3232

33+
const desiredTimeForPreview = 14;
34+
3335
const decomposedScene = decomposeScene(scene);
3436

3537
const mergedMesh = mergeMeshesToGeometry(decomposedScene.meshes);
@@ -159,7 +161,6 @@ export function makeRenderingPipeline({
159161
}
160162

161163
function setPreviewBufferDimensions() {
162-
const desiredTimeForPreview = 10;
163164
const numPixelsForPreview = desiredTimeForPreview / tileRender.getTimePerPixel();
164165

165166
const aspectRatio = screenWidth / screenHeight;

src/renderer/glsl/chunks/random.glsl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,20 @@ float randomSample() {
3333
vec2 randomSampleVec2() {
3434
return vec2(randomSample(), randomSample());
3535
}
36+
37+
struct MaterialSamples {
38+
vec2 s1;
39+
vec2 s2;
40+
vec2 s3;
41+
};
42+
43+
MaterialSamples getRandomMaterialSamples() {
44+
MaterialSamples samples;
45+
46+
samples.s1 = randomSampleVec2();
47+
samples.s2 = randomSampleVec2();
48+
samples.s3 = randomSampleVec2();
49+
50+
return samples;
51+
}
3652
`;

src/renderer/glsl/chunks/rayTraceCore.glsl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ export default `
44
#define THICK_GLASS 2
55
#define SHADOW_CATCHER 3
66

7-
#define SAMPLES_PER_MATERIAL 8
8-
97
const float IOR = 1.5;
108
const float INV_IOR = 1.0 / IOR;
119

@@ -71,11 +69,11 @@ export default `
7169
struct Path {
7270
Ray ray;
7371
vec3 li;
74-
vec3 albedo;
7572
float alpha;
7673
vec3 beta;
7774
bool specularBounce;
7875
bool abort;
76+
float misWeight;
7977
};
8078

8179
uniform Camera camera;

src/renderer/glsl/chunks/sampleGlassSpecular.glsl

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ export default `
33
#ifdef USE_GLASS
44

55
void sampleGlassSpecular(SurfaceInteraction si, int bounce, inout Path path) {
6+
bool lastBounce = bounce == BOUNCES;
67
vec3 viewDir = -path.ray.d;
78
float cosTheta = dot(si.normal, viewDir);
89

10+
MaterialSamples samples = getRandomMaterialSamples();
11+
12+
float reflectionOrRefraction = samples.s1.x;
13+
914
float F = si.materialType == THIN_GLASS ?
1015
fresnelSchlick(abs(cosTheta), R0) : // thin glass
1116
fresnelSchlickTIR(cosTheta, R0, IOR); // thick glass
1217

1318
vec3 lightDir;
1419

15-
float reflectionOrRefraction = randomSample();
16-
1720
if (reflectionOrRefraction < F) {
1821
lightDir = reflect(-viewDir, si.normal);
1922
} else {
@@ -23,13 +26,13 @@ void sampleGlassSpecular(SurfaceInteraction si, int bounce, inout Path path) {
2326
path.beta *= si.color;
2427
}
2528

29+
path.misWeight = 1.0;
30+
2631
initRay(path.ray, si.position + EPS * lightDir, lightDir);
2732

28-
// advance sample index by unused stratified samples
29-
const int usedSamples = 1;
30-
sampleIndex += SAMPLES_PER_MATERIAL - usedSamples;
33+
path.li += lastBounce ? path.beta * sampleBackgroundFromDirection(lightDir) : vec3(0.0);
3134

32-
path.li += bounce == BOUNCES ? path.beta * sampleBackgroundFromDirection(lightDir) : vec3(0.0);
35+
path.specularBounce = true;
3336
}
3437

3538
#endif

src/renderer/glsl/chunks/sampleMaterial.glsl

Lines changed: 61 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -3,123 +3,106 @@
33

44
export default `
55

6-
vec3 importanceSampleLight(SurfaceInteraction si, vec3 viewDir, bool lastBounce, vec2 random) {
7-
vec3 li;
6+
void sampleMaterial(SurfaceInteraction si, int bounce, inout Path path) {
7+
bool lastBounce = bounce == BOUNCES;
8+
mat3 basis = orthonormalBasis(si.normal);
9+
vec3 viewDir = -path.ray.d;
810

9-
float lightPdf;
10-
vec2 uv;
11-
vec3 lightDir = sampleEnvmap(random, uv, lightPdf);
11+
MaterialSamples samples = getRandomMaterialSamples();
1212

13-
float cosThetaL = dot(si.normal, lightDir);
13+
vec2 diffuseOrSpecular = samples.s1;
14+
vec2 lightDirSample = samples.s2;
15+
vec2 bounceDirSample = samples.s3;
1416

15-
float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
16-
if (orientation < 0.0) {
17-
return li;
18-
}
17+
// Step 1: Add direct illumination of the light source (the hdr map)
18+
// On every bounce but the last, importance sample the light source
19+
// On the last bounce, multiple importance sample the brdf AND the light source, determined by random var
1920

20-
float diffuseWeight = 1.0;
21-
Ray ray;
22-
initRay(ray, si.position + EPS * lightDir, lightDir);
23-
if (intersectSceneShadow(ray)) {
24-
if (lastBounce) {
25-
diffuseWeight = 0.0;
26-
} else {
27-
return li;
28-
}
21+
vec3 lightDir;
22+
vec2 uv;
23+
float lightPdf;
24+
bool brdfSample = false;
25+
26+
if (lastBounce && diffuseOrSpecular.x < 0.5) {
27+
// reuse this sample by multiplying by 2 to bring sample from [0, 0.5), to [0, 1)
28+
lightDir = 2.0 * diffuseOrSpecular.x < mix(0.5, 0.0, si.metalness) ?
29+
lightDirDiffuse(si.faceNormal, viewDir, basis, lightDirSample) :
30+
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, lightDirSample);
31+
32+
uv = cartesianToEquirect(lightDir);
33+
lightPdf = envmapPdf(uv);
34+
brdfSample = true;
35+
} else {
36+
lightDir = sampleEnvmap(lightDirSample, uv, lightPdf);
2937
}
3038

31-
vec3 irr = textureLinear(envmap, uv).xyz;
32-
33-
float scatteringPdf;
34-
vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, diffuseWeight, scatteringPdf);
35-
36-
float weight = powerHeuristic(lightPdf, scatteringPdf);
37-
38-
li = brdf * irr * abs(cosThetaL) * weight / lightPdf;
39-
40-
return li;
41-
}
42-
43-
vec3 importanceSampleMaterial(SurfaceInteraction si, vec3 viewDir, bool lastBounce, vec3 lightDir) {
44-
vec3 li;
45-
4639
float cosThetaL = dot(si.normal, lightDir);
4740

41+
float occluded = 1.0;
42+
4843
float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
4944
if (orientation < 0.0) {
50-
return li;
45+
// light dir points towards surface. invalid dir.
46+
occluded = 0.0;
5147
}
5248

5349
float diffuseWeight = 1.0;
54-
Ray ray;
55-
initRay(ray, si.position + EPS * lightDir, lightDir);
56-
if (intersectSceneShadow(ray)) {
50+
51+
initRay(path.ray, si.position + EPS * lightDir, lightDir);
52+
if (intersectSceneShadow(path.ray)) {
5753
if (lastBounce) {
5854
diffuseWeight = 0.0;
5955
} else {
60-
return li;
56+
occluded = 0.0;
6157
}
6258
}
6359

64-
vec2 uv = cartesianToEquirect(lightDir);
65-
66-
float lightPdf = envmapPdf(uv);
67-
6860
vec3 irr = textureLinear(envmap, uv).rgb;
6961

7062
float scatteringPdf;
7163
vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, diffuseWeight, scatteringPdf);
7264

73-
float weight = powerHeuristic(scatteringPdf, lightPdf);
74-
75-
li += brdf * irr * abs(cosThetaL) * weight / scatteringPdf;
76-
77-
return li;
78-
}
65+
float weight;
66+
if (lastBounce) {
67+
weight = brdfSample ?
68+
2.0 * powerHeuristic(scatteringPdf, lightPdf) / scatteringPdf :
69+
2.0 * powerHeuristic(lightPdf, scatteringPdf) / lightPdf;
70+
} else {
71+
weight = powerHeuristic(lightPdf, scatteringPdf) / lightPdf;
72+
}
7973

80-
void sampleMaterial(SurfaceInteraction si, int bounce, inout Path path) {
81-
mat3 basis = orthonormalBasis(si.normal);
82-
vec3 viewDir = -path.ray.d;
74+
path.li += path.beta * occluded * brdf * irr * abs(cosThetaL) * weight;;
8375

84-
vec2 diffuseOrSpecular = randomSampleVec2();
76+
// Step 2: Setup ray direction for next bounce by importance sampling the BRDF
8577

86-
vec3 lightDir = diffuseOrSpecular.x < mix(0.5, 0.0, si.metalness) ?
87-
lightDirDiffuse(si.faceNormal, viewDir, basis, randomSampleVec2()) :
88-
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, randomSampleVec2());
78+
if (lastBounce) {
79+
return;
80+
}
8981

90-
bool lastBounce = bounce == BOUNCES;
82+
lightDir = diffuseOrSpecular.y < mix(0.5, 0.0, si.metalness) ?
83+
lightDirDiffuse(si.faceNormal, viewDir, basis, bounceDirSample) :
84+
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, bounceDirSample);
9185

92-
// Add path contribution
93-
path.li += path.beta * (
94-
importanceSampleLight(si, viewDir, lastBounce, randomSampleVec2()) +
95-
importanceSampleMaterial(si, viewDir, lastBounce, lightDir)
96-
);
86+
cosThetaL = dot(si.normal, lightDir);
9787

98-
// Get new path direction
88+
orientation = dot(si.faceNormal, viewDir) * cosThetaL;
89+
path.abort = orientation < 0.0;
9990

100-
if (lastBounce) {
91+
if (path.abort) {
10192
return;
10293
}
10394

104-
lightDir = diffuseOrSpecular.y < mix(0.5, 0.0, si.metalness) ?
105-
lightDirDiffuse(si.faceNormal, viewDir, basis, randomSampleVec2()) :
106-
lightDirSpecular(si.faceNormal, viewDir, basis, si.roughness, randomSampleVec2());
95+
brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, 1.0, scatteringPdf);
10796

108-
float cosThetaL = dot(si.normal, lightDir);
97+
uv = cartesianToEquirect(lightDir);
98+
lightPdf = envmapPdf(uv);
10999

110-
float scatteringPdf;
111-
vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, 1.0, scatteringPdf);
100+
path.misWeight = powerHeuristic(scatteringPdf, lightPdf);
112101

113102
path.beta *= abs(cosThetaL) * brdf / scatteringPdf;
114103

115-
initRay(path.ray, si.position + EPS * lightDir, lightDir);
116-
117-
// If new ray direction is pointing into the surface,
118-
// the light path is physically impossible and we terminate the path.
119-
float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
120-
path.abort = orientation < 0.0;
121-
122104
path.specularBounce = false;
123-
}
124105

106+
initRay(path.ray, si.position + EPS * lightDir, lightDir);
107+
}
125108
`;

0 commit comments

Comments
 (0)