Skip to content

Commit d177f44

Browse files
Changes to the way lobes are picked for sampling
1 parent a48c2ac commit d177f44

File tree

1 file changed

+42
-36
lines changed

1 file changed

+42
-36
lines changed

src/shaders/common/disney.glsl

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,10 @@ vec3 ToLocal(vec3 X, vec3 Y, vec3 Z, vec3 V)
4343
return vec3(dot(V, X), dot(V, Y), dot(V, Z));
4444
}
4545

46-
float FresnelMix(Material mat, float eta, float VDotH)
46+
float DisneyFresnel(Material mat, float eta, float LDotH, float VDotH)
4747
{
48-
float metallicFresnel = SchlickFresnel(VDotH);
49-
float dielectricFresnel = DielectricFresnel(VDotH, eta);
48+
float metallicFresnel = SchlickFresnel(LDotH);
49+
float dielectricFresnel = DielectricFresnel(abs(VDotH), eta);
5050
return mix(dielectricFresnel, metallicFresnel, mat.metallic);
5151
}
5252

@@ -72,7 +72,7 @@ vec3 EvalDiffuse(Material mat, vec3 Csheen, vec3 V, vec3 L, vec3 H, out float pd
7272
vec3 Fsheen = FH * mat.sheen * Csheen;
7373

7474
pdf = L.z * INV_PI;
75-
return (INV_PI * mix(Fd, ss, mat.subsurface) * mat.baseColor + Fsheen) * (1.0 - mat.metallic) * (1.0 - mat.specTrans);
75+
return (1.0 - mat.metallic) * (1.0 - mat.specTrans) * (INV_PI * mix(Fd, ss, mat.subsurface) * mat.baseColor + Fsheen);
7676
}
7777

7878
vec3 EvalSpecReflection(Material mat, float eta, vec3 specCol, vec3 V, vec3 L, vec3 H, out float pdf)
@@ -81,14 +81,13 @@ vec3 EvalSpecReflection(Material mat, float eta, vec3 specCol, vec3 V, vec3 L, v
8181
if (L.z <= 0.0)
8282
return vec3(0.0);
8383

84-
float FM = FresnelMix(mat, eta, dot(L, H));
84+
float FM = DisneyFresnel(mat, eta, dot(L, H), dot(V, H));
8585
vec3 F = mix(specCol, vec3(1.0), FM);
8686
float D = GTR2(H.z, mat.roughness);
8787
float G1 = SmithG(abs(V.z), mat.roughness);
8888
float G2 = G1 * SmithG(abs(L.z), mat.roughness);
89-
float jacobian = 1.0 / (4.0 * dot(V, H));
9089

91-
pdf = G1 * max(0.0, dot(V, H)) * D * jacobian / V.z;
90+
pdf = G1 * D / (4.0 * V.z);
9291
return F * D * G2 / (4.0 * L.z * V.z);
9392
}
9493

@@ -104,12 +103,12 @@ vec3 EvalSpecRefraction(Material mat, float eta, vec3 V, vec3 L, vec3 H, out flo
104103
denom *= denom;
105104
float G1 = SmithG(abs(V.z), mat.roughness);
106105
float G2 = G1 * SmithG(abs(L.z), mat.roughness);
106+
float eta2 = eta * eta;
107107
float jacobian = abs(dot(L, H)) / denom;
108108

109109
pdf = G1 * max(0.0, dot(V, H)) * D * jacobian / V.z;
110110

111-
vec3 specColor = pow(mat.baseColor, vec3(0.5));
112-
return specColor * (1.0 - mat.metallic) * mat.specTrans * (1.0 - F) * D * G2 * abs(dot(V, H)) * abs(dot(L, H)) * eta * eta / (denom * abs(L.z) * abs(V.z));
111+
return pow(mat.baseColor, vec3(0.5)) * (1.0 - mat.metallic) * mat.specTrans * (1.0 - F) * D * G2 * abs(dot(V, H)) * jacobian * eta2 / abs(L.z * V.z);
113112
}
114113

115114
vec3 EvalClearcoat(Material mat, vec3 V, vec3 L, vec3 H, out float pdf)
@@ -143,7 +142,7 @@ void GetLobeProbabilities(Material mat, float eta, vec3 specCol, float approxFre
143142
diffuseWt = Luminance(mat.baseColor) * (1.0 - mat.metallic) * (1.0 - mat.specTrans);
144143
specReflectWt = Luminance(mix(specCol, vec3(1.0), approxFresnel));
145144
specRefractWt = (1.0 - approxFresnel) * (1.0 - mat.metallic) * mat.specTrans * Luminance(mat.baseColor);
146-
clearcoatWt = mat.clearcoat * (1.0 - mat.metallic);
145+
clearcoatWt = 0.25 * mat.clearcoat * (1.0 - mat.metallic);
147146
float totalWt = diffuseWt + specReflectWt + specRefractWt + clearcoatWt;
148147

149148
diffuseWt /= totalWt;
@@ -170,17 +169,16 @@ vec3 DisneySample(State state, vec3 V, vec3 N, out vec3 L, out float pdf)
170169

171170
// Lobe weights
172171
float diffuseWt, specReflectWt, specRefractWt, clearcoatWt;
173-
// TODO: Recheck fresnel. Not sure if correct. VDotN produces fireflies with rough dielectric.
174-
// VDotH matches Mitsuba and gets rid of all fireflies but H isn't available at this stage
175-
float approxFresnel = FresnelMix(state.mat, state.eta, V.z);
172+
// Note: Fresnel is approx and based on N and not H since H isn't available at this stage.
173+
float approxFresnel = DisneyFresnel(state.mat, state.eta, V.z, V.z);
176174
GetLobeProbabilities(state.mat, state.eta, specCol, approxFresnel, diffuseWt, specReflectWt, specRefractWt, clearcoatWt);
177175

178176
// CDF for picking a lobe
179177
float cdf[4];
180178
cdf[0] = diffuseWt;
181-
cdf[1] = cdf[0] + specReflectWt;
182-
cdf[2] = cdf[1] + specRefractWt;
183-
cdf[3] = cdf[2] + clearcoatWt;
179+
cdf[1] = cdf[0] + clearcoatWt;
180+
cdf[2] = cdf[1] + specReflectWt;
181+
cdf[3] = cdf[2] + specRefractWt;
184182

185183
if (r1 < cdf[0]) // Diffuse Reflection Lobe
186184
{
@@ -192,44 +190,52 @@ vec3 DisneySample(State state, vec3 V, vec3 N, out vec3 L, out float pdf)
192190
f = EvalDiffuse(state.mat, sheenCol, V, L, H, pdf);
193191
pdf *= diffuseWt;
194192
}
195-
else if (r1 < cdf[1]) // Specular Reflection Lobe
193+
else if (r1 < cdf[1]) // Clearcoat Lobe
196194
{
197195
r1 = (r1 - cdf[0]) / (cdf[1] - cdf[0]);
198-
vec3 H = SampleGGXVNDF(V, state.mat.roughness, r1, r2);
196+
197+
vec3 H = SampleGTR1(state.mat.clearcoatRoughness, r1, r2);
199198

200199
if (H.z < 0.0)
201200
H = -H;
202201

203202
L = normalize(reflect(-V, H));
204203

205-
f = EvalSpecReflection(state.mat, state.eta, specCol, V, L, H, pdf);
206-
pdf *= specReflectWt;
204+
f = EvalClearcoat(state.mat, V, L, H, pdf);
205+
pdf *= clearcoatWt;
207206
}
208-
else if (r1 < cdf[2]) // Specular Refraction Lobe
207+
else // Specular Reflection/Refraction Lobes
209208
{
210-
r1 = (r1 - cdf[1]) / (cdf[2] - cdf[1]);
209+
r1 = (r1 - cdf[1]) / (1.0 - cdf[1]);
211210
vec3 H = SampleGGXVNDF(V, state.mat.roughness, r1, r2);
212211

213212
if (H.z < 0.0)
214213
H = -H;
215214

216-
L = normalize(refract(-V, H, state.eta));
215+
// Using fresnel based on half vector to pick between refl and refr lobes
216+
// as doing it this way gets rid of the fireflies due to incorrect weighting
217+
// when picking lobes based on shading normal. This split isn't required in DisneyEval()
218+
// as H is already available when computing the lobe probabilities.
217219

218-
f = EvalSpecRefraction(state.mat, state.eta, V, L, H, pdf);
219-
pdf *= specRefractWt;
220-
}
221-
else // Clearcoat Lobe
222-
{
223-
r1 = (r1 - cdf[2]) / (1.0 - cdf[2]);
224-
vec3 H = SampleGTR1(state.mat.clearcoatRoughness, r1, r2);
220+
float fresnel = DisneyFresnel(state.mat, state.eta, dot(L, H), dot(V, H));
221+
float F = 1.0 - ((1.0 - fresnel) * state.mat.specTrans * (1.0 - state.mat.metallic));
225222

226-
if (H.z < 0.0)
227-
H = -H;
223+
if (rand() < F)
224+
{
225+
L = normalize(reflect(-V, H));
228226

229-
L = normalize(reflect(-V, H));
227+
f = EvalSpecReflection(state.mat, state.eta, specCol, V, L, H, pdf);
228+
pdf *= F;
229+
}
230+
else
231+
{
232+
L = normalize(refract(-V, H, state.eta));
230233

231-
f = EvalClearcoat(state.mat, V, L, H, pdf);
232-
pdf *= clearcoatWt;
234+
f = EvalSpecRefraction(state.mat, state.eta, V, L, H, pdf);
235+
pdf *= 1.0 - F;
236+
}
237+
238+
pdf *= specReflectWt + specRefractWt;
233239
}
234240

235241
L = ToWorld(T, B, N, L);
@@ -261,7 +267,7 @@ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, out float bsdfPdf)
261267

262268
// Lobe weights
263269
float diffuseWt, specReflectWt, specRefractWt, clearcoatWt;
264-
float fresnel = FresnelMix(state.mat, state.eta, dot(V, H));
270+
float fresnel = DisneyFresnel(state.mat, state.eta, dot(L, H), dot(V, H));
265271
GetLobeProbabilities(state.mat, state.eta, specCol, fresnel, diffuseWt, specReflectWt, specRefractWt, clearcoatWt);
266272

267273
float pdf;

0 commit comments

Comments
 (0)