Skip to content

Commit 17eb961

Browse files
Move shading from world space to tangent space
1 parent edeb393 commit 17eb961

File tree

2 files changed

+77
-44
lines changed

2 files changed

+77
-44
lines changed

src/shaders/common/disney.glsl

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -33,83 +33,93 @@
3333
* [8] https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
3434
*/
3535

36+
vec3 ToWorld(vec3 X, vec3 Y, vec3 Z, vec3 V)
37+
{
38+
return V.x * X + V.y * Y + V.z * Z;
39+
}
40+
41+
vec3 ToLocal(vec3 X, vec3 Y, vec3 Z, vec3 V)
42+
{
43+
return vec3(dot(V, X), dot(V, Y), dot(V, Z));
44+
}
45+
3646
float FresnelMix(Material mat, float eta, vec3 V, vec3 L, vec3 H)
3747
{
3848
float metallicFresnel = SchlickFresnel(abs(dot(L, H)));
3949
float dielectricFresnel = DielectricFresnel(abs(dot(V, H)), eta);
4050
return mix(dielectricFresnel, metallicFresnel, mat.metallic);
4151
}
4252

43-
vec3 EvalDiffuse(Material mat, vec3 Csheen, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
53+
vec3 EvalDiffuse(Material mat, vec3 Csheen, vec3 V, vec3 L, vec3 H, inout float pdf)
4454
{
4555
pdf = 0.0;
46-
if (dot(N, L) <= 0.0)
56+
if (L.z <= 0.0)
4757
return vec3(0.0);
4858

4959
// Diffuse
50-
float FL = SchlickFresnel(dot(N, L));
51-
float FV = SchlickFresnel(dot(N, V));
60+
float FL = SchlickFresnel(L.z);
61+
float FV = SchlickFresnel(V.z);
5262
float FH = SchlickFresnel(dot(L, H));
5363
float Fd90 = 0.5 + 2.0 * dot(L, H) * dot(L, H) * mat.roughness;
5464
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
5565

5666
// Fake Subsurface TODO: Replace with volumetric scattering
5767
float Fss90 = dot(L, H) * dot(L, H) * mat.roughness;
5868
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
59-
float ss = 1.25 * (Fss * (1.0 / (dot(N, L) + dot(N, V)) - 0.5) + 0.5);
69+
float ss = 1.25 * (Fss * (1.0 / (L.z + V.z) - 0.5) + 0.5);
6070

6171
// Sheen
6272
vec3 Fsheen = FH * mat.sheen * Csheen;
6373

64-
pdf = dot(N, L) * INV_PI;
74+
pdf = L.z * INV_PI;
6575
return (INV_PI * mix(Fd, ss, mat.subsurface) * mat.baseColor + Fsheen) * (1.0 - mat.metallic) * (1.0 - mat.specTrans);
6676
}
6777

68-
vec3 EvalSpecReflection(Material mat, float eta, vec3 specCol, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
78+
vec3 EvalSpecReflection(Material mat, float eta, vec3 specCol, vec3 V, vec3 L, vec3 H, inout float pdf)
6979
{
7080
pdf = 0.0;
71-
if (dot(N, L) <= 0.0)
81+
if (L.z <= 0.0)
7282
return vec3(0.0);
7383

7484
float FM = FresnelMix(mat, eta, V, L, H);
7585
vec3 F = mix(specCol, vec3(1.0), FM);
76-
float D = GTR2(dot(N, H), mat.roughness);
77-
float G = SmithG(abs(dot(N, L)), mat.roughness)
78-
* SmithG(abs(dot(N, V)), mat.roughness);
86+
float D = GTR2(H.z, mat.roughness);
87+
float G = SmithG(abs(L.z), mat.roughness)
88+
* SmithG(abs(V.z), mat.roughness);
7989

80-
pdf = D * dot(N, H) / (4.0 * abs(dot(V, H)));
90+
pdf = D * H.z / (4.0 * abs(dot(V, H)));
8191
return F * D * G;
8292
}
8393

84-
vec3 EvalSpecRefraction(Material mat, float eta, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
94+
vec3 EvalSpecRefraction(Material mat, float eta, vec3 V, vec3 L, vec3 H, inout float pdf)
8595
{
8696
pdf = 0.0;
87-
if (dot(N, L) >= 0.0)
97+
if (L.z >= 0.0)
8898
return vec3(0.0);
8999

90100
float F = DielectricFresnel(abs(dot(V, H)), eta);
91-
float D = GTR2(dot(N, H), mat.roughness);
101+
float D = GTR2(H.z, mat.roughness);
92102
float denomSqrt = dot(L, H) + dot(V, H) * eta;
93-
float G = SmithG(abs(dot(N, L)), mat.roughness)
94-
* SmithG(abs(dot(N, V)), mat.roughness);
103+
float G = SmithG(abs(L.z), mat.roughness)
104+
* SmithG(abs(V.z), mat.roughness);
95105

96-
pdf = D * dot(N, H) * abs(dot(L, H)) / (denomSqrt * denomSqrt);
106+
pdf = D * H.z * abs(dot(L, H)) / (denomSqrt * denomSqrt);
97107
vec3 specColor = pow(mat.baseColor, vec3(0.5));
98108
return specColor * (1.0 - mat.metallic) * mat.specTrans * (1.0 - F) * D * G * abs(dot(V, H)) * abs(dot(L, H)) * eta * eta * 4.0 / (denomSqrt * denomSqrt);
99109
}
100110

101-
vec3 EvalClearcoat(Material mat, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
111+
vec3 EvalClearcoat(Material mat, vec3 V, vec3 L, vec3 H, inout float pdf)
102112
{
103113
pdf = 0.0;
104-
if (dot(N, L) <= 0.0)
114+
if (L.z <= 0.0)
105115
return vec3(0.0);
106116

107117
float FH = DielectricFresnel(dot(V, H), 1.0 / 1.5);
108118
float F = mix(0.04, 1.0, FH);
109-
float D = GTR1(dot(N, H), mat.clearcoatRoughness);
110-
float G = SmithG(dot(N, L), 0.25)
111-
* SmithG(dot(N, V), 0.25);
112-
pdf = D * dot(N, H) / (4.0 * dot(V, H));
119+
float D = GTR1(H.z, mat.clearcoatRoughness);
120+
float G = SmithG(L.z, 0.25)
121+
* SmithG(V.z, 0.25);
122+
pdf = D * H.z / (4.0 * dot(V, H));
113123
return vec3(0.25 * mat.clearcoat * F * D * G);
114124
}
115125

@@ -162,61 +172,60 @@ vec3 DisneySample(State state, vec3 V, vec3 N, inout vec3 L, inout float pdf)
162172

163173
vec3 T, B;
164174
Onb(N, T, B);
175+
// NDotL = L.z; NDotV = V.z; NDotH = H.z
176+
V = ToLocal(T, B, N, V);
165177

166178
if(r1 < cdf[0]) // Diffuse Reflection Lobe
167179
{
168180
r1 /= cdf[0];
169181
L = CosineSampleHemisphere(r1, r2);
170-
L = T * L.x + B * L.y + N * L.z;
171182

172183
vec3 H = normalize(L + V);
173184

174-
f = EvalDiffuse(state.mat, sheenCol, V, N, L, H, pdf);
185+
f = EvalDiffuse(state.mat, sheenCol, V, L, H, pdf);
175186
pdf *= diffuseWt;
176187
}
177188
else if (r1 < cdf[1]) // Specular Reflection Lobe
178189
{
179190
r1 = (r1 - cdf[0]) / (cdf[1] - cdf[0]);
180191
vec3 H = SampleGTR2(state.mat.roughness, r1, r2);
181-
H = T * H.x + B * H.y + N * H.z;
182192

183193
if (dot(V, H) < 0.0)
184194
H = -H;
185195

186196
L = normalize(reflect(-V, H));
187197

188-
f = EvalSpecReflection(state.mat, state.eta, specCol, V, N, L, H, pdf);
198+
f = EvalSpecReflection(state.mat, state.eta, specCol, V, L, H, pdf);
189199
pdf *= specReflectWt;
190200
}
191201
else if (r1 < cdf[2]) // Specular Refraction Lobe
192202
{
193203
r1 = (r1 - cdf[1]) / (cdf[2] - cdf[1]);
194204
vec3 H = SampleGTR2(state.mat.roughness, r1, r2);
195-
H = T * H.x + B * H.y + N * H.z;
196205

197206
if (dot(V, H) < 0.0)
198207
H = -H;
199208

200209
L = normalize(refract(-V, H, state.eta));
201210

202-
f = EvalSpecRefraction(state.mat, state.eta, V, N, L, H, pdf);
211+
f = EvalSpecRefraction(state.mat, state.eta, V, L, H, pdf);
203212
pdf *= specRefractWt;
204213
}
205214
else // Clearcoat Lobe
206215
{
207216
r1 = (r1 - cdf[2]) / (1.0 - cdf[2]);
208217
vec3 H = SampleGTR1(state.mat.clearcoatRoughness, r1, r2);
209-
H = T * H.x + B * H.y + N * H.z;
210218

211219
if (dot(V, H) < 0.0)
212220
H = -H;
213221

214222
L = normalize(reflect(-V, H));
215223

216-
f = EvalClearcoat(state.mat, V, N, L, H, pdf);
224+
f = EvalClearcoat(state.mat, V, L, H, pdf);
217225
pdf *= clearcoatWt;
218226
}
219227

228+
L = ToWorld(T, B, N, L);
220229
return f * abs(dot(N, L));
221230
}
222231

@@ -225,11 +234,14 @@ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, inout float bsdfPdf)
225234
bsdfPdf = 0.0;
226235
vec3 f = vec3(0.0);
227236

228-
float NDotL = dot(N, L);
229-
float NDotV = dot(N, V);
237+
vec3 T, B;
238+
Onb(N, T, B);
239+
// NDotL = L.z; NDotV = V.z; NDotH = H.z
240+
V = ToLocal(T, B, N, V);
241+
L = ToLocal(T, B, N, L);
230242

231243
vec3 H;
232-
if (NDotL > 0.0)
244+
if (L.z > 0.0)
233245
H = normalize(L + V);
234246
else
235247
H = normalize(L + V * state.eta);
@@ -249,32 +261,32 @@ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, inout float bsdfPdf)
249261
float pdf;
250262

251263
// Diffuse
252-
if (diffuseWt > 0.0 && NDotL > 0.0)
264+
if (diffuseWt > 0.0 && L.z > 0.0)
253265
{
254-
f += EvalDiffuse(state.mat, sheenCol, V, N, L, H, pdf);
266+
f += EvalDiffuse(state.mat, sheenCol, V, L, H, pdf);
255267
bsdfPdf += pdf * diffuseWt;
256268
}
257269

258270
// Specular Reflection
259-
if (specReflectWt > 0.0 && NDotL > 0.0 && NDotV > 0.0)
271+
if (specReflectWt > 0.0 && L.z > 0.0 && V.z > 0.0)
260272
{
261-
f += EvalSpecReflection(state.mat, state.eta, specCol, V, N, L, H, pdf);
273+
f += EvalSpecReflection(state.mat, state.eta, specCol, V, L, H, pdf);
262274
bsdfPdf += pdf * specReflectWt;
263275
}
264276

265277
// Specular Refraction
266-
if (specRefractWt > 0.0 && NDotL < 0.0)
278+
if (specRefractWt > 0.0 && L.z < 0.0)
267279
{
268-
f += EvalSpecRefraction(state.mat, state.eta, V, N, L, H, pdf);
280+
f += EvalSpecRefraction(state.mat, state.eta, V, L, H, pdf);
269281
bsdfPdf += pdf * specRefractWt;
270282
}
271283

272284
// Clearcoat
273-
if (clearcoatWt > 0.0 && NDotL > 0.0 && NDotV > 0.0)
285+
if (clearcoatWt > 0.0 && L.z > 0.0 && V.z > 0.0)
274286
{
275-
f += EvalClearcoat(state.mat, V, N, L, H, pdf);
287+
f += EvalClearcoat(state.mat, V, L, H, pdf);
276288
bsdfPdf += pdf * clearcoatWt;
277289
}
278290

279-
return f * abs(dot(N, L));
291+
return f * abs(L.z);
280292
}

src/shaders/common/sampling.glsl

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,27 @@ vec3 SampleGTR2(float rgh, float r1, float r2)
6767
return vec3(sinTheta * cosPhi, sinTheta * sinPhi, cosTheta);
6868
}
6969

70+
vec3 sampleGGXVNDF(vec3 V, float rgh, float r1, float r2)
71+
{
72+
73+
vec3 Vh = normalize(vec3(rgh * V.x, rgh * V.y, V.z));
74+
75+
float lensq = Vh.x * Vh.x + Vh.y * Vh.y;
76+
vec3 T1 = lensq > 0 ? vec3(-Vh.y, Vh.x, 0) * inversesqrt(lensq) : vec3(1, 0, 0);
77+
vec3 T2 = cross(Vh, T1);
78+
79+
float r = sqrt(r1);
80+
float phi = 2.0 * M_PI * r2;
81+
float t1 = r * cos(phi);
82+
float t2 = r * sin(phi);
83+
float s = 0.5 * (1.0 + Vh.z);
84+
t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2;
85+
86+
vec3 Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * Vh;
87+
88+
return normalize(vec3(rgh * Nh.x, rgh * Nh.y, std::max<float>(0.0, Nh.z)));
89+
}
90+
7091
float GTR2Aniso(float NDotH, float HDotX, float HDotY, float ax, float ay)
7192
{
7293
float a = HDotX / ax;

0 commit comments

Comments
 (0)