31
31
* [6] http://shihchinw.github.io/2015/07/implementing-disney-principled-brdf-in-arnold.html
32
32
* [7] https://github.com/mmp/pbrt-v4/blob/0ec29d1ec8754bddd9d667f0e80c4ff025c900ce/src/pbrt/bxdfs.cpp#L76-L286
33
33
* [8] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
34
- * [9] https://graphics.pixar.com/library/RadiosityCaching/paper.pdf
35
34
*/
36
35
37
36
// -----------------------------------------------------------------------
38
- void EvalDielectricReflection(State state, inout BsdfSampleRec bRec )
37
+ vec3 EvalDielectricReflection(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf )
39
38
// -----------------------------------------------------------------------
40
39
{
41
- if (dot (bRec. N, bRec. L) < 0.0 ) return ;
40
+ if (dot (N, L) < 0.0 ) return vec3 ( 0.0 ) ;
42
41
43
- float F = DielectricFresnel(dot (bRec. V, bRec. H), state.eta);
44
- float D = GTR2(dot (bRec. N, bRec. H), state.mat.roughness);
42
+ float F = DielectricFresnel(dot (V, H), state.eta);
43
+ float D = GTR2(dot (N, H), state.mat.roughness);
45
44
46
- bRec. pdf = D * dot (bRec. N, bRec. H) * F / (4.0 * dot (bRec. V, bRec. H));
45
+ pdf = D * dot (N, H) * F / (4.0 * dot (V, H));
47
46
48
- float G = SmithG_GGX(abs (dot (bRec. N, bRec. L)), state.mat.roughness) * SmithG_GGX(dot (bRec. N, bRec. V), state.mat.roughness);
49
- bRec.f = state.mat.albedo * F * D * G;
47
+ float G = SmithG_GGX(abs (dot (N, L)), state.mat.roughness) * SmithG_GGX(dot (N, V), state.mat.roughness);
48
+ return state.mat.albedo * F * D * G;
50
49
}
51
50
52
51
// -----------------------------------------------------------------------
53
- void EvalDielectricRefraction(State state, inout BsdfSampleRec bRec )
52
+ vec3 EvalDielectricRefraction(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf )
54
53
// -----------------------------------------------------------------------
55
54
{
56
- float F = DielectricFresnel(abs (dot (bRec. V, bRec. H)), state.eta);
57
- float D = GTR2(dot (bRec. N, bRec. H), state.mat.roughness);
55
+ float F = DielectricFresnel(abs (dot (V, H)), state.eta);
56
+ float D = GTR2(dot (N, H), state.mat.roughness);
58
57
59
- float denomSqrt = dot (bRec. L, bRec. H) * state.eta + dot (bRec. V, bRec. H);
60
- bRec. pdf = D * dot (bRec. N, bRec. H) * (1.0 - F) * abs (dot (bRec. L, bRec. H)) / (denomSqrt * denomSqrt);
58
+ float denomSqrt = dot (L, H) * state.eta + dot (V, H);
59
+ pdf = D * dot (N, H) * (1.0 - F) * abs (dot (L, H)) / (denomSqrt * denomSqrt);
61
60
62
- float G = SmithG_GGX(abs (dot (bRec. N, bRec. L)), state.mat.roughness) * SmithG_GGX(dot (bRec. N, bRec. V), state.mat.roughness);
63
- bRec.f = state.mat.albedo * (1.0 - F) * D * G * abs (dot (bRec. V, bRec. H)) * abs (dot (bRec. L, bRec. H)) * 4.0 * state.eta * state.eta / (denomSqrt * denomSqrt);
61
+ float G = SmithG_GGX(abs (dot (N, L)), state.mat.roughness) * SmithG_GGX(dot (N, V), state.mat.roughness);
62
+ return state.mat.albedo * (1.0 - F) * D * G * abs (dot (V, H)) * abs (dot (L, H)) * 4.0 * state.eta * state.eta / (denomSqrt * denomSqrt);
64
63
}
65
64
66
65
// -----------------------------------------------------------------------
67
- void EvalSpecular(State state, inout BsdfSampleRec bRec , vec3 Cspec0 )
66
+ vec3 EvalSpecular(State state, vec3 Cspec0, vec3 V , vec3 N, vec3 L, vec3 H, inout float pdf )
68
67
// -----------------------------------------------------------------------
69
68
{
70
- if (dot (bRec. N, bRec. L) < 0.0 ) return ;
69
+ if (dot (N, L) < 0.0 ) return vec3 ( 0.0 ) ;
71
70
72
- float D = GTR2_aniso(dot (bRec. N, bRec. H), dot (bRec. H, state.tangent), dot (bRec. H, state.bitangent), state.mat.ax, state.mat.ay);
73
- bRec. pdf = D * dot (bRec. N, bRec. H) / (4.0 * dot (bRec. V, bRec. H));
71
+ float D = GTR2_aniso(dot (N, H), dot (H, state.tangent), dot (H, state.bitangent), state.mat.ax, state.mat.ay);
72
+ pdf = D * dot (N, H) / (4.0 * dot (V, H));
74
73
75
- float FH = SchlickFresnel(dot (bRec. L, bRec. H));
74
+ float FH = SchlickFresnel(dot (L, H));
76
75
vec3 F = mix (Cspec0, vec3 (1.0 ), FH);
77
- float G = SmithG_GGX_aniso(dot (bRec. N, bRec. L), dot (bRec. L, state.tangent), dot (bRec. L, state.bitangent), state.mat.ax, state.mat.ay);
78
- G *= SmithG_GGX_aniso(dot (bRec. N, bRec. V), dot (bRec. V, state.tangent), dot (bRec. V, state.bitangent), state.mat.ax, state.mat.ay);
79
- bRec.f = F * D * G;
76
+ float G = SmithG_GGX_aniso(dot (N, L), dot (L, state.tangent), dot (L, state.bitangent), state.mat.ax, state.mat.ay);
77
+ G *= SmithG_GGX_aniso(dot (N, V), dot (V, state.tangent), dot (V, state.bitangent), state.mat.ax, state.mat.ay);
78
+ return F * D * G;
80
79
}
81
80
82
81
// -----------------------------------------------------------------------
83
- void EvalClearcoat(State state, inout BsdfSampleRec bRec )
82
+ vec3 EvalClearcoat(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf )
84
83
// -----------------------------------------------------------------------
85
84
{
86
- if (dot (bRec. N, bRec. L) < 0.0 ) return ;
85
+ if (dot (N, L) < 0.0 ) return vec3 ( 0.0 ) ;
87
86
88
- float D = GTR1(dot (bRec. N, bRec. H), state.mat.clearcoatRoughness);
89
- bRec. pdf = D * dot (bRec. N, bRec. H) / (4.0 * dot (bRec. V, bRec. H));
87
+ float D = GTR1(dot (N, H), state.mat.clearcoatRoughness);
88
+ pdf = D * dot (N, H) / (4.0 * dot (V, H));
90
89
91
- float FH = SchlickFresnel(dot (bRec. L, bRec. H));
90
+ float FH = SchlickFresnel(dot (L, H));
92
91
float F = mix (0.04 , 1.0 , FH);
93
- float G = SmithG_GGX(dot (bRec. N, bRec. L), 0.25 ) * SmithG_GGX(dot (bRec. N, bRec. V), 0.25 );
94
- bRec.f = vec3 (0.25 * state.mat.clearcoat * F * D * G);
92
+ float G = SmithG_GGX(dot (N, L), 0.25 ) * SmithG_GGX(dot (N, V), 0.25 );
93
+ return vec3 (0.25 * state.mat.clearcoat * F * D * G);
95
94
}
96
95
97
96
// -----------------------------------------------------------------------
98
- void EvalDiffuse(State state, inout BsdfSampleRec bRec , vec3 Csheen )
97
+ vec3 EvalDiffuse(State state, vec3 Csheen, vec3 V , vec3 N, vec3 L, vec3 H, inout float pdf )
99
98
// -----------------------------------------------------------------------
100
99
{
101
- if (dot (bRec. N, bRec. L) < 0.0 ) return ;
100
+ if (dot (N, L) < 0.0 ) return vec3 ( 0.0 ) ;
102
101
103
- bRec. pdf = dot (bRec. N, bRec. L) * (1.0 / PI);
102
+ pdf = dot (N, L) * (1.0 / PI);
104
103
105
- float FL = SchlickFresnel(dot (bRec. N, bRec. L));
106
- float FV = SchlickFresnel(dot (bRec. N, bRec. V));
107
- float FH = SchlickFresnel(dot (bRec. L, bRec. H));
108
- float Fd90 = 0.5 + 2.0 * dot (bRec. L, bRec. H) * dot (bRec. L, bRec. H) * state.mat.roughness;
104
+ float FL = SchlickFresnel(dot (N, L));
105
+ float FV = SchlickFresnel(dot (N, V));
106
+ float FH = SchlickFresnel(dot (L, H));
107
+ float Fd90 = 0.5 + 2.0 * dot (L, H) * dot (L, H) * state.mat.roughness;
109
108
float Fd = mix (1.0 , Fd90, FL) * mix (1.0 , Fd90, FV);
110
109
vec3 Fsheen = FH * state.mat.sheen * Csheen;
111
- bRec.f = ((1.0 / PI) * Fd * (1.0 - state.mat.subsurface) * state.mat.albedo + Fsheen) * (1.0 - state.mat.metallic);
110
+ return ((1.0 / PI) * Fd * (1.0 - state.mat.subsurface) * state.mat.albedo + Fsheen) * (1.0 - state.mat.metallic);
112
111
}
113
112
114
113
// -----------------------------------------------------------------------
115
- void EvalSubsurface(State state, inout BsdfSampleRec bRec )
114
+ vec3 EvalSubsurface(State state, vec3 V, vec3 N, vec3 L, inout float pdf )
116
115
// -----------------------------------------------------------------------
117
116
{
118
- // if (dot(bRec.N, bRec.V) < 0.0) return ;
117
+ pdf = ( 1.0 / TWO_PI) ;
119
118
120
- bRec.pdf = (1.0 / TWO_PI);
121
-
122
- float FL = SchlickFresnel(abs (dot (bRec.N, bRec.L)));
123
- float FV = SchlickFresnel(dot (bRec.N, bRec.V));
119
+ float FL = SchlickFresnel(abs (dot (N, L)));
120
+ float FV = SchlickFresnel(dot (N, V));
124
121
float Fd = (1 .0f - 0 .5f * FL) * (1 .0f - 0 .5f * FV);
125
- bRec.f = sqrt (state.mat.albedo) * state.mat.subsurface * (1.0 / PI) * Fd * (1.0 - state.mat.metallic);
122
+ return sqrt (state.mat.albedo) * state.mat.subsurface * (1.0 / PI) * Fd * (1.0 - state.mat.metallic) * ( 1.0 - state.mat.specTrans );
126
123
}
127
124
128
125
// -----------------------------------------------------------------------
129
- void DisneySample(inout State state, inout BsdfSampleRec bRec )
126
+ vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float pdf )
130
127
// -----------------------------------------------------------------------
131
128
{
132
129
state.isSubsurface = false;
133
- bRec. pdf = 0.0 ;
134
- bRec.L = vec3 (0.0 );
130
+ pdf = 0.0 ;
131
+ vec3 f = vec3 (0.0 );
135
132
136
133
float r1 = rand();
137
134
float r2 = rand();
138
135
139
- vec3 brdf = vec3 (0.0 );
140
- vec3 bsdf = vec3 (0.0 );
141
- float brdfPdf = 0.0 ;
142
- float bsdfPdf = 0.0 ;
143
-
144
136
float diffuseRatio = 0.5 * (1.0 - state.mat.metallic);
145
137
float transWeight = (1.0 - state.mat.metallic) * state.mat.specTrans;
146
138
@@ -155,52 +147,50 @@ void DisneySample(inout State state, inout BsdfSampleRec bRec)
155
147
if (rand() < transWeight)
156
148
{
157
149
vec3 H = ImportanceSampleGTR2(state.mat.roughness, r1, r2);
158
- bRec. H = state.tangent * H.x + state.bitangent * H.y + bRec. N * H.z;
150
+ H = state.tangent * H.x + state.bitangent * H.y + N * H.z;
159
151
160
- vec3 R = reflect (- bRec. V, bRec. H);
161
- float F = DielectricFresnel(abs (dot (R, bRec. H)), state.eta);
152
+ vec3 R = reflect (- V, H);
153
+ float F = DielectricFresnel(abs (dot (R, H)), state.eta);
162
154
163
155
// Reflection/Total internal reflection
164
156
if (rand() < F)
165
157
{
166
- bRec. L = normalize (R);
167
- EvalDielectricReflection(state, bRec );
158
+ L = normalize (R);
159
+ f = EvalDielectricReflection(state, V, N, L, H, pdf );
168
160
}
169
161
else // Transmission
170
162
{
171
- bRec. L = normalize (refract (- bRec. V, bRec. H, state.eta));
172
- EvalDielectricRefraction(state, bRec );
163
+ L = normalize (refract (- V, H, state.eta));
164
+ f = EvalDielectricRefraction(state, V, N, L, H, pdf );
173
165
}
174
166
175
- bsdf = bRec.f ;
176
- bsdfPdf = bRec.pdf ;
167
+ f *= transWeight ;
168
+ pdf *= transWeight ;
177
169
}
178
170
else // BRDF
179
171
{
180
172
if (rand() < diffuseRatio)
181
173
{
182
- // This way of performing subsurface scattering was taken from Tinsel [4] and is similar [9].
183
- // Simpler than random walk but not sure if accurate.
174
+ // Diffuse transmission. A way to approximate subsurface scattering
184
175
if (rand() < state.mat.subsurface)
185
176
{
186
- vec3 L = UniformSampleHemisphere(r1, r2);
187
- bRec. L = state.tangent * L.x + state.bitangent * L.y - bRec. N * L.z;
177
+ L = UniformSampleHemisphere(r1, r2);
178
+ L = state.tangent * L.x + state.bitangent * L.y - N * L.z;
188
179
189
- EvalSubsurface(state, bRec );
190
- bRec. pdf *= state.mat.subsurface * diffuseRatio;
180
+ f = EvalSubsurface(state, V, N, L, pdf );
181
+ pdf *= state.mat.subsurface * diffuseRatio;
191
182
192
183
state.isSubsurface = true; // Required when sampling lights from inside surface
193
- // state.specularBounce = true;
194
184
}
195
185
else // Diffuse
196
186
{
197
- vec3 L = CosineSampleHemisphere(r1, r2);
198
- bRec. L = state.tangent * L.x + state.bitangent * L.y + bRec. N * L.z;
187
+ L = CosineSampleHemisphere(r1, r2);
188
+ L = state.tangent * L.x + state.bitangent * L.y + N * L.z;
199
189
200
- bRec. H = normalize (bRec. L + bRec. V);
190
+ vec3 H = normalize (L + V);
201
191
202
- EvalDiffuse(state, bRec, Csheen );
203
- bRec. pdf *= (1.0 - state.mat.subsurface) * diffuseRatio;
192
+ f = EvalDiffuse(state, Csheen, V, N, L, H, pdf );
193
+ pdf *= (1.0 - state.mat.subsurface) * diffuseRatio;
204
194
}
205
195
}
206
196
else // Specular
@@ -212,79 +202,78 @@ void DisneySample(inout State state, inout BsdfSampleRec bRec)
212
202
{
213
203
// TODO: Implement http://jcgt.org/published/0007/04/01/
214
204
vec3 H = ImportanceSampleGTR2_aniso(state.mat.ax, state.mat.ay, r1, r2);
215
- bRec. H = state.tangent * H.x + state.bitangent * H.y + bRec. N * H.z;
216
- bRec. L = normalize (reflect (- bRec. V, bRec. H));
205
+ H = state.tangent * H.x + state.bitangent * H.y + N * H.z;
206
+ L = normalize (reflect (- V, H));
217
207
218
- EvalSpecular(state, bRec, Cspec0 );
219
- bRec. pdf *= primarySpecRatio * (1.0 - diffuseRatio);
208
+ f = EvalSpecular(state, Cspec0, V, N, L, H, pdf );
209
+ pdf *= primarySpecRatio * (1.0 - diffuseRatio);
220
210
}
221
211
else // Sample clearcoat lobe
222
212
{
223
213
vec3 H = ImportanceSampleGTR1(state.mat.clearcoatRoughness, r1, r2);
224
- bRec. H = state.tangent * H.x + state.bitangent * H.y + bRec. N * H.z;
225
- bRec. L = normalize (reflect (- bRec. V, bRec. H));
214
+ H = state.tangent * H.x + state.bitangent * H.y + N * H.z;
215
+ L = normalize (reflect (- V, H));
226
216
227
- EvalClearcoat(state, bRec );
228
- bRec. pdf *= (1.0 - primarySpecRatio) * (1.0 - diffuseRatio);
217
+ f = EvalClearcoat(state, V, N, L, H, pdf );
218
+ pdf *= (1.0 - primarySpecRatio) * (1.0 - diffuseRatio);
229
219
}
230
220
}
231
- brdf = bRec.f;
232
- brdfPdf = bRec.pdf;
233
- }
234
221
235
- bRec.pdf = mix (brdfPdf, bsdfPdf, transWeight);
236
- bRec.f = mix (brdf, bsdf, transWeight);
222
+ f *= (1.0 - transWeight);
223
+ pdf *= (1.0 - transWeight);
224
+ }
225
+ return f;
237
226
}
238
227
239
228
// -----------------------------------------------------------------------
240
- void DisneyEval(State state, inout BsdfSampleRec bRec )
229
+ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, inout float pdf )
241
230
// -----------------------------------------------------------------------
242
231
{
243
- if (dot (bRec.N, bRec.L) < 0.0 )
244
- bRec.H = normalize (bRec.L * (1.0 / state.eta) + bRec.V);
232
+ vec3 H;
233
+
234
+ if (dot (N, L) < 0.0 )
235
+ H = normalize (L * (1.0 / state.eta) + V);
245
236
else
246
- bRec. H = normalize (bRec. L + bRec. V);
237
+ H = normalize (L + V);
247
238
248
- if (dot (bRec.N, bRec.H) < 0.0 )
249
- bRec.H = - bRec.H;
239
+ if (dot (N, H) < 0.0 )
240
+ H = - H;
241
+
242
+ float diffuseRatio = 0.5 * (1.0 - state.mat.metallic);
243
+ float primarySpecRatio = 1.0 / (1.0 + state.mat.clearcoat);
244
+ float transWeight = (1.0 - state.mat.metallic) * state.mat.specTrans;
250
245
251
246
vec3 brdf = vec3 (0.0 );
252
247
vec3 bsdf = vec3 (0.0 );
253
248
float brdfPdf = 0.0 ;
254
249
float bsdfPdf = 0.0 ;
255
250
256
- float diffuseRatio = 0.5 * (1.0 - state.mat.metallic);
257
- float primarySpecRatio = 1.0 / (1.0 + state.mat.clearcoat);
258
- float transWeight = (1.0 - state.mat.metallic) * state.mat.specTrans;
259
-
260
251
// BSDF
261
252
if (transWeight > 0.0 )
262
253
{
263
254
// Transmission
264
- if (dot (bRec. N, bRec. L) < 0.0 )
255
+ if (dot (N, L) < 0.0 )
265
256
{
266
- EvalDielectricRefraction(state, bRec );
257
+ bsdf = EvalDielectricRefraction(state, V, N, L, H, bsdfPdf );
267
258
}
268
259
else // Reflection
269
260
{
270
- EvalDielectricReflection(state, bRec );
261
+ bsdf = EvalDielectricReflection(state, V, N, L, H, bsdfPdf );
271
262
}
272
-
273
- bsdf += bRec.f;
274
- bsdfPdf += bRec.pdf;
275
263
}
276
264
265
+ float m_pdf;
266
+
277
267
if (transWeight < 1.0 )
278
268
{
279
269
// Subsurface
280
- if (dot (bRec. N, bRec. L) < 0.0 )
270
+ if (dot (N, L) < 0.0 )
281
271
{
282
- // TODO: Double check this. Causing occassional NaNs and fails furnace test when used with transmission
272
+ // TODO: Double check this. Fails furnace test when used with rough transmission
283
273
if (state.mat.subsurface > 0.0 )
284
274
{
285
- EvalSubsurface(state, bRec);
286
- brdf += bRec.f;
287
- brdfPdf += bRec.pdf * state.mat.subsurface * diffuseRatio;
275
+ brdf = EvalSubsurface(state, V, N, L, m_pdf);
276
+ brdfPdf = m_pdf * state.mat.subsurface * diffuseRatio;
288
277
}
289
278
}
290
279
// BRDF
@@ -298,22 +287,19 @@ void DisneyEval(State state, inout BsdfSampleRec bRec)
298
287
vec3 Csheen = mix (vec3 (1.0 ), Ctint, state.mat.sheenTint);
299
288
300
289
// Diffuse
301
- EvalDiffuse(state, bRec, Csheen);
302
- brdf += bRec.f;
303
- brdfPdf += bRec.pdf * (1.0 - state.mat.subsurface) * diffuseRatio;
290
+ brdf += EvalDiffuse(state, Csheen, V, N, L, H, m_pdf);
291
+ brdfPdf += m_pdf * (1.0 - state.mat.subsurface) * diffuseRatio;
304
292
305
293
// Specular
306
- EvalSpecular(state, bRec, Cspec0);
307
- brdf += bRec.f;
308
- brdfPdf += bRec.pdf * primarySpecRatio * (1.0 - diffuseRatio);
294
+ brdf += EvalSpecular(state, Cspec0, V, N, L, H, m_pdf);
295
+ brdfPdf += m_pdf * primarySpecRatio * (1.0 - diffuseRatio);
309
296
310
297
// Clearcoat
311
- EvalClearcoat(state, bRec);
312
- brdf += bRec.f;
313
- brdfPdf += bRec.pdf * (1.0 - primarySpecRatio) * (1.0 - diffuseRatio);
298
+ brdf += EvalClearcoat(state, V, N, L, H, m_pdf);
299
+ brdfPdf += m_pdf * (1.0 - primarySpecRatio) * (1.0 - diffuseRatio);
314
300
}
315
301
}
316
302
317
- bRec. pdf = mix (brdfPdf, bsdfPdf, transWeight);
318
- bRec.f = mix (brdf, bsdf, transWeight);
303
+ pdf = mix (brdfPdf, bsdfPdf, transWeight);
304
+ return mix (brdf, bsdf, transWeight);
319
305
}
0 commit comments