@@ -43,10 +43,10 @@ vec3 ToLocal(vec3 X, vec3 Y, vec3 Z, vec3 V)
43
43
return vec3 (dot (V, X), dot (V, Y), dot (V, Z));
44
44
}
45
45
46
- float FresnelMix (Material mat, float eta, float VDotH)
46
+ float DisneyFresnel (Material mat, float eta, float LDotH , float VDotH)
47
47
{
48
- float metallicFresnel = SchlickFresnel(VDotH );
49
- float dielectricFresnel = DielectricFresnel(VDotH, eta);
48
+ float metallicFresnel = SchlickFresnel(LDotH );
49
+ float dielectricFresnel = DielectricFresnel(abs ( VDotH) , eta);
50
50
return mix (dielectricFresnel, metallicFresnel, mat.metallic);
51
51
}
52
52
@@ -72,7 +72,7 @@ vec3 EvalDiffuse(Material mat, vec3 Csheen, vec3 V, vec3 L, vec3 H, out float pd
72
72
vec3 Fsheen = FH * mat.sheen * Csheen;
73
73
74
74
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 );
76
76
}
77
77
78
78
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
81
81
if (L.z <= 0.0 )
82
82
return vec3 (0.0 );
83
83
84
- float FM = FresnelMix (mat, eta, dot (L, H));
84
+ float FM = DisneyFresnel (mat, eta, dot (L, H), dot (V , H));
85
85
vec3 F = mix (specCol, vec3 (1.0 ), FM);
86
86
float D = GTR2(H.z, mat.roughness);
87
87
float G1 = SmithG(abs (V.z), mat.roughness);
88
88
float G2 = G1 * SmithG(abs (L.z), mat.roughness);
89
- float jacobian = 1.0 / (4.0 * dot (V, H));
90
89
91
- pdf = G1 * max ( 0.0 , dot (V, H)) * D * jacobian / V.z;
90
+ pdf = G1 * D / ( 4.0 * V.z) ;
92
91
return F * D * G2 / (4.0 * L.z * V.z);
93
92
}
94
93
@@ -104,12 +103,12 @@ vec3 EvalSpecRefraction(Material mat, float eta, vec3 V, vec3 L, vec3 H, out flo
104
103
denom *= denom;
105
104
float G1 = SmithG(abs (V.z), mat.roughness);
106
105
float G2 = G1 * SmithG(abs (L.z), mat.roughness);
106
+ float eta2 = eta * eta;
107
107
float jacobian = abs (dot (L, H)) / denom;
108
108
109
109
pdf = G1 * max (0.0 , dot (V, H)) * D * jacobian / V.z;
110
110
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);
113
112
}
114
113
115
114
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
143
142
diffuseWt = Luminance(mat.baseColor) * (1.0 - mat.metallic) * (1.0 - mat.specTrans);
144
143
specReflectWt = Luminance(mix (specCol, vec3 (1.0 ), approxFresnel));
145
144
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);
147
146
float totalWt = diffuseWt + specReflectWt + specRefractWt + clearcoatWt;
148
147
149
148
diffuseWt /= totalWt;
@@ -170,17 +169,16 @@ vec3 DisneySample(State state, vec3 V, vec3 N, out vec3 L, out float pdf)
170
169
171
170
// Lobe weights
172
171
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);
176
174
GetLobeProbabilities(state.mat, state.eta, specCol, approxFresnel, diffuseWt, specReflectWt, specRefractWt, clearcoatWt);
177
175
178
176
// CDF for picking a lobe
179
177
float cdf[4 ];
180
178
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 ;
184
182
185
183
if (r1 < cdf[0 ]) // Diffuse Reflection Lobe
186
184
{
@@ -192,44 +190,52 @@ vec3 DisneySample(State state, vec3 V, vec3 N, out vec3 L, out float pdf)
192
190
f = EvalDiffuse(state.mat, sheenCol, V, L, H, pdf);
193
191
pdf *= diffuseWt;
194
192
}
195
- else if (r1 < cdf[1 ]) // Specular Reflection Lobe
193
+ else if (r1 < cdf[1 ]) // Clearcoat Lobe
196
194
{
197
195
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);
199
198
200
199
if (H.z < 0.0 )
201
200
H = - H;
202
201
203
202
L = normalize (reflect (- V, H));
204
203
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 ;
207
206
}
208
- else if (r1 < cdf[ 2 ]) // Specular Refraction Lobe
207
+ else // Specular Reflection/ Refraction Lobes
209
208
{
210
- r1 = (r1 - cdf[1 ]) / (cdf[ 2 ] - cdf[1 ]);
209
+ r1 = (r1 - cdf[1 ]) / (1.0 - cdf[1 ]);
211
210
vec3 H = SampleGGXVNDF(V, state.mat.roughness, r1, r2);
212
211
213
212
if (H.z < 0.0 )
214
213
H = - H;
215
214
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.
217
219
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));
225
222
226
- if (H.z < 0.0 )
227
- H = - H;
223
+ if (rand() < F)
224
+ {
225
+ L = normalize (reflect (- V, H));
228
226
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));
230
233
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;
233
239
}
234
240
235
241
L = ToWorld(T, B, N, L);
@@ -261,7 +267,7 @@ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, out float bsdfPdf)
261
267
262
268
// Lobe weights
263
269
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));
265
271
GetLobeProbabilities(state.mat, state.eta, specCol, fresnel, diffuseWt, specReflectWt, specRefractWt, clearcoatWt);
266
272
267
273
float pdf;
0 commit comments