|
3 | 3 |
|
4 | 4 | export default `
|
5 | 5 |
|
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; |
8 | 10 |
|
9 |
| - float lightPdf; |
10 |
| - vec2 uv; |
11 |
| - vec3 lightDir = sampleEnvmap(random, uv, lightPdf); |
| 11 | + MaterialSamples samples = getRandomMaterialSamples(); |
12 | 12 |
|
13 |
| - float cosThetaL = dot(si.normal, lightDir); |
| 13 | + vec2 diffuseOrSpecular = samples.s1; |
| 14 | + vec2 lightDirSample = samples.s2; |
| 15 | + vec2 bounceDirSample = samples.s3; |
14 | 16 |
|
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 |
19 | 20 |
|
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); |
29 | 37 | }
|
30 | 38 |
|
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 |
| - |
46 | 39 | float cosThetaL = dot(si.normal, lightDir);
|
47 | 40 |
|
| 41 | + float occluded = 1.0; |
| 42 | + |
48 | 43 | float orientation = dot(si.faceNormal, viewDir) * cosThetaL;
|
49 | 44 | if (orientation < 0.0) {
|
50 |
| - return li; |
| 45 | + // light dir points towards surface. invalid dir. |
| 46 | + occluded = 0.0; |
51 | 47 | }
|
52 | 48 |
|
53 | 49 | 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)) { |
57 | 53 | if (lastBounce) {
|
58 | 54 | diffuseWeight = 0.0;
|
59 | 55 | } else {
|
60 |
| - return li; |
| 56 | + occluded = 0.0; |
61 | 57 | }
|
62 | 58 | }
|
63 | 59 |
|
64 |
| - vec2 uv = cartesianToEquirect(lightDir); |
65 |
| - |
66 |
| - float lightPdf = envmapPdf(uv); |
67 |
| - |
68 | 60 | vec3 irr = textureLinear(envmap, uv).rgb;
|
69 | 61 |
|
70 | 62 | float scatteringPdf;
|
71 | 63 | vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, diffuseWeight, scatteringPdf);
|
72 | 64 |
|
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 | + } |
79 | 73 |
|
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;; |
83 | 75 |
|
84 |
| - vec2 diffuseOrSpecular = randomSampleVec2(); |
| 76 | + // Step 2: Setup ray direction for next bounce by importance sampling the BRDF |
85 | 77 |
|
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 | + } |
89 | 81 |
|
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); |
91 | 85 |
|
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); |
97 | 87 |
|
98 |
| - // Get new path direction |
| 88 | + orientation = dot(si.faceNormal, viewDir) * cosThetaL; |
| 89 | + path.abort = orientation < 0.0; |
99 | 90 |
|
100 |
| - if (lastBounce) { |
| 91 | + if (path.abort) { |
101 | 92 | return;
|
102 | 93 | }
|
103 | 94 |
|
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); |
107 | 96 |
|
108 |
| - float cosThetaL = dot(si.normal, lightDir); |
| 97 | + uv = cartesianToEquirect(lightDir); |
| 98 | + lightPdf = envmapPdf(uv); |
109 | 99 |
|
110 |
| - float scatteringPdf; |
111 |
| - vec3 brdf = materialBrdf(si, viewDir, lightDir, cosThetaL, 1.0, scatteringPdf); |
| 100 | + path.misWeight = powerHeuristic(scatteringPdf, lightPdf); |
112 | 101 |
|
113 | 102 | path.beta *= abs(cosThetaL) * brdf / scatteringPdf;
|
114 | 103 |
|
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 |
| - |
122 | 104 | path.specularBounce = false;
|
123 |
| -} |
124 | 105 |
|
| 106 | + initRay(path.ray, si.position + EPS * lightDir, lightDir); |
| 107 | +} |
125 | 108 | `;
|
0 commit comments