Skip to content

Commit ca8f088

Browse files
Merge pull request #31 from knightcrawler25/dev
BSDF Refactoring + fixes
2 parents cf29907 + 92ab56f commit ca8f088

File tree

4 files changed

+121
-152
lines changed

4 files changed

+121
-152
lines changed

src/shaders/common/disney.glsl

Lines changed: 105 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -31,116 +31,108 @@
3131
* [6] http://shihchinw.github.io/2015/07/implementing-disney-principled-brdf-in-arnold.html
3232
* [7] https://github.com/mmp/pbrt-v4/blob/0ec29d1ec8754bddd9d667f0e80c4ff025c900ce/src/pbrt/bxdfs.cpp#L76-L286
3333
* [8] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
34-
* [9] https://graphics.pixar.com/library/RadiosityCaching/paper.pdf
3534
*/
3635

3736
//-----------------------------------------------------------------------
38-
void EvalDielectricReflection(State state, inout BsdfSampleRec bRec)
37+
vec3 EvalDielectricReflection(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
3938
//-----------------------------------------------------------------------
4039
{
41-
if (dot(bRec.N, bRec.L) < 0.0) return;
40+
if (dot(N, L) < 0.0) return vec3(0.0);
4241

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);
4544

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));
4746

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;
5049
}
5150

5251
//-----------------------------------------------------------------------
53-
void EvalDielectricRefraction(State state, inout BsdfSampleRec bRec)
52+
vec3 EvalDielectricRefraction(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
5453
//-----------------------------------------------------------------------
5554
{
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);
5857

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);
6160

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);
6463
}
6564

6665
//-----------------------------------------------------------------------
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)
6867
//-----------------------------------------------------------------------
6968
{
70-
if (dot(bRec.N, bRec.L) < 0.0) return;
69+
if (dot(N, L) < 0.0) return vec3(0.0);
7170

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));
7473

75-
float FH = SchlickFresnel(dot(bRec.L, bRec.H));
74+
float FH = SchlickFresnel(dot(L, H));
7675
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;
8079
}
8180

8281
//-----------------------------------------------------------------------
83-
void EvalClearcoat(State state, inout BsdfSampleRec bRec)
82+
vec3 EvalClearcoat(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
8483
//-----------------------------------------------------------------------
8584
{
86-
if (dot(bRec.N, bRec.L) < 0.0) return;
85+
if (dot(N, L) < 0.0) return vec3(0.0);
8786

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));
9089

91-
float FH = SchlickFresnel(dot(bRec.L, bRec.H));
90+
float FH = SchlickFresnel(dot(L, H));
9291
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);
9594
}
9695

9796
//-----------------------------------------------------------------------
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)
9998
//-----------------------------------------------------------------------
10099
{
101-
if (dot(bRec.N, bRec.L) < 0.0) return;
100+
if (dot(N, L) < 0.0) return vec3(0.0);
102101

103-
bRec.pdf = dot(bRec.N, bRec.L) * (1.0 / PI);
102+
pdf = dot(N, L) * (1.0 / PI);
104103

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;
109108
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
110109
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);
112111
}
113112

114113
//-----------------------------------------------------------------------
115-
void EvalSubsurface(State state, inout BsdfSampleRec bRec)
114+
vec3 EvalSubsurface(State state, vec3 V, vec3 N, vec3 L, inout float pdf)
116115
//-----------------------------------------------------------------------
117116
{
118-
//if (dot(bRec.N, bRec.V) < 0.0) return;
117+
pdf = (1.0 / TWO_PI);
119118

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));
124121
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);
126123
}
127124

128125
//-----------------------------------------------------------------------
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)
130127
//-----------------------------------------------------------------------
131128
{
132129
state.isSubsurface = false;
133-
bRec.pdf = 0.0;
134-
bRec.L = vec3(0.0);
130+
pdf = 0.0;
131+
vec3 f = vec3(0.0);
135132

136133
float r1 = rand();
137134
float r2 = rand();
138135

139-
vec3 brdf = vec3(0.0);
140-
vec3 bsdf = vec3(0.0);
141-
float brdfPdf = 0.0;
142-
float bsdfPdf = 0.0;
143-
144136
float diffuseRatio = 0.5 * (1.0 - state.mat.metallic);
145137
float transWeight = (1.0 - state.mat.metallic) * state.mat.specTrans;
146138

@@ -155,52 +147,50 @@ void DisneySample(inout State state, inout BsdfSampleRec bRec)
155147
if (rand() < transWeight)
156148
{
157149
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;
159151

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);
162154

163155
// Reflection/Total internal reflection
164156
if (rand() < F)
165157
{
166-
bRec.L = normalize(R);
167-
EvalDielectricReflection(state, bRec);
158+
L = normalize(R);
159+
f = EvalDielectricReflection(state, V, N, L, H, pdf);
168160
}
169161
else // Transmission
170162
{
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);
173165
}
174166

175-
bsdf = bRec.f;
176-
bsdfPdf = bRec.pdf;
167+
f *= transWeight;
168+
pdf *= transWeight;
177169
}
178170
else // BRDF
179171
{
180172
if (rand() < diffuseRatio)
181173
{
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
184175
if (rand() < state.mat.subsurface)
185176
{
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;
188179

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;
191182

192183
state.isSubsurface = true; // Required when sampling lights from inside surface
193-
//state.specularBounce = true;
194184
}
195185
else // Diffuse
196186
{
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;
199189

200-
bRec.H = normalize(bRec.L + bRec.V);
190+
vec3 H = normalize(L + V);
201191

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;
204194
}
205195
}
206196
else // Specular
@@ -212,79 +202,78 @@ void DisneySample(inout State state, inout BsdfSampleRec bRec)
212202
{
213203
// TODO: Implement http://jcgt.org/published/0007/04/01/
214204
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));
217207

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);
220210
}
221211
else // Sample clearcoat lobe
222212
{
223213
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));
226216

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);
229219
}
230220
}
231-
brdf = bRec.f;
232-
brdfPdf = bRec.pdf;
233-
}
234221

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;
237226
}
238227

239228
//-----------------------------------------------------------------------
240-
void DisneyEval(State state, inout BsdfSampleRec bRec)
229+
vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, inout float pdf)
241230
//-----------------------------------------------------------------------
242231
{
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);
245236
else
246-
bRec.H = normalize(bRec.L + bRec.V);
237+
H = normalize(L + V);
247238

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;
250245

251246
vec3 brdf = vec3(0.0);
252247
vec3 bsdf = vec3(0.0);
253248
float brdfPdf = 0.0;
254249
float bsdfPdf = 0.0;
255250

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-
260251
// BSDF
261252
if (transWeight > 0.0)
262253
{
263254
// Transmission
264-
if (dot(bRec.N, bRec.L) < 0.0)
255+
if (dot(N, L) < 0.0)
265256
{
266-
EvalDielectricRefraction(state, bRec);
257+
bsdf = EvalDielectricRefraction(state, V, N, L, H, bsdfPdf);
267258
}
268259
else // Reflection
269260
{
270-
EvalDielectricReflection(state, bRec);
261+
bsdf = EvalDielectricReflection(state, V, N, L, H, bsdfPdf);
271262
}
272-
273-
bsdf += bRec.f;
274-
bsdfPdf += bRec.pdf;
275263
}
276264

265+
float m_pdf;
266+
277267
if (transWeight < 1.0)
278268
{
279269
// Subsurface
280-
if (dot(bRec.N, bRec.L) < 0.0)
270+
if (dot(N, L) < 0.0)
281271
{
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
283273
if (state.mat.subsurface > 0.0)
284274
{
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;
288277
}
289278
}
290279
// BRDF
@@ -298,22 +287,19 @@ void DisneyEval(State state, inout BsdfSampleRec bRec)
298287
vec3 Csheen = mix(vec3(1.0), Ctint, state.mat.sheenTint);
299288

300289
// 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;
304292

305293
// 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);
309296

310297
// 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);
314300
}
315301
}
316302

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);
319305
}

0 commit comments

Comments
 (0)