Skip to content

Commit 64477d3

Browse files
Fixes + validation against PBRT
-Lights can now cast shadows when lit by other lights. -Ray offset fix for dielectrics+NEE. -Temporarily removed anisotropic parameter from UI since sampling was incorrect.
1 parent dd0a02d commit 64477d3

17 files changed

+104
-47
lines changed

src/Main.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ void SaveFrame(const std::string filename)
121121
int w, h;
122122
renderer->GetOutputBuffer(&data, w, h);
123123
stbi_flip_vertically_on_write(true);
124-
stbi_write_bmp(filename.c_str(), w, h, 3, data);
124+
stbi_write_png(filename.c_str(), w, h, 3, data, w*3);
125125
delete data;
126126
}
127127

@@ -276,7 +276,7 @@ void MainLoop(void* arg)
276276

277277
if (ImGui::Button("Save Screenshot"))
278278
{
279-
SaveFrame("./img_" + to_string(renderer->GetSampleCount()) + ".jpg");
279+
SaveFrame("./img_" + to_string(renderer->GetSampleCount()) + ".png");
280280
}
281281

282282
std::vector<const char*> scenes;
@@ -378,7 +378,7 @@ void MainLoop(void* arg)
378378
objectPropChanged |= ImGui::SliderFloat("Specular", &scene->materials[scene->meshInstances[selectedInstance].materialID].specular, 0.0f, 1.0f);
379379
objectPropChanged |= ImGui::SliderFloat("SpecularTint", &scene->materials[scene->meshInstances[selectedInstance].materialID].specularTint, 0.0f, 1.0f);
380380
objectPropChanged |= ImGui::SliderFloat("Subsurface", &scene->materials[scene->meshInstances[selectedInstance].materialID].subsurface, 0.0f, 1.0f);
381-
objectPropChanged |= ImGui::SliderFloat("Anisotropic", &scene->materials[scene->meshInstances[selectedInstance].materialID].anisotropic, 0.0f, 1.0f);
381+
//objectPropChanged |= ImGui::SliderFloat("Anisotropic", &scene->materials[scene->meshInstances[selectedInstance].materialID].anisotropic, 0.0f, 1.0f);
382382
objectPropChanged |= ImGui::SliderFloat("Sheen", &scene->materials[scene->meshInstances[selectedInstance].materialID].sheen, 0.0f, 1.0f);
383383
objectPropChanged |= ImGui::SliderFloat("SheenTint", &scene->materials[scene->meshInstances[selectedInstance].materialID].sheenTint, 0.0f, 1.0f);
384384
objectPropChanged |= ImGui::SliderFloat("Clearcoat", &scene->materials[scene->meshInstances[selectedInstance].materialID].clearcoat, 0.0f, 1.0f);

src/shaders/common/anyhit.glsl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,45 @@
2626
bool AnyHit(Ray r, float maxDist)
2727
//-----------------------------------------------------------------------
2828
{
29+
30+
#ifdef LIGHTS
31+
// Intersect Emitters
32+
for (int i = 0; i < numOfLights; i++)
33+
{
34+
// Fetch light Data
35+
vec3 position = texelFetch(lightsTex, ivec2(i * 5 + 0, 0), 0).xyz;
36+
vec3 emission = texelFetch(lightsTex, ivec2(i * 5 + 1, 0), 0).xyz;
37+
vec3 u = texelFetch(lightsTex, ivec2(i * 5 + 2, 0), 0).xyz;
38+
vec3 v = texelFetch(lightsTex, ivec2(i * 5 + 3, 0), 0).xyz;
39+
vec3 params = texelFetch(lightsTex, ivec2(i * 5 + 4, 0), 0).xyz;
40+
float radius = params.x;
41+
float area = params.y;
42+
float type = params.z;
43+
44+
// Intersect rectangular area light
45+
if (type == QUAD_LIGHT)
46+
{
47+
vec3 normal = normalize(cross(u, v));
48+
vec4 plane = vec4(normal, dot(normal, position));
49+
u *= 1.0f / dot(u, u);
50+
v *= 1.0f / dot(v, v);
51+
52+
float d = RectIntersect(position, u, v, plane, r);
53+
if (d > 0.0 && d < maxDist)
54+
return true;
55+
}
56+
57+
// Intersect spherical area light
58+
if (type == SPHERE_LIGHT)
59+
{
60+
float d = SphereIntersect(radius, position, r);
61+
if (d > 0.0 && d < maxDist)
62+
return true;
63+
}
64+
}
65+
#endif
66+
67+
// Intersect BVH and tris
2968
int stack[64];
3069
int ptr = 0;
3170
stack[ptr++] = -1;

src/shaders/common/closest_hit.glsl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ float ClosestHit(Ray r, inout State state, inout LightSampleRec lightSampleRec)
4343
float area = params.y;
4444
float type = params.z;
4545

46-
// Rectangular Area Light
46+
// Intersect rectangular area light
4747
if (type == 0.)
4848
{
4949
vec3 normal = normalize(cross(u, v));
@@ -67,7 +67,7 @@ float ClosestHit(Ray r, inout State state, inout LightSampleRec lightSampleRec)
6767
}
6868
}
6969

70-
// Spherical Area Light
70+
// Intersect spherical area light
7171
if (type == 1.)
7272
{
7373
d = SphereIntersect(radius, position, r);
@@ -85,6 +85,7 @@ float ClosestHit(Ray r, inout State state, inout LightSampleRec lightSampleRec)
8585
}
8686
#endif
8787

88+
// Intersect BVH and tris
8889
int stack[64];
8990
int ptr = 0;
9091
stack[ptr++] = -1;

src/shaders/common/disney.glsl

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,11 @@
3737
vec3 EvalDielectricReflection(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
3838
//-----------------------------------------------------------------------
3939
{
40-
if (dot(N, L) < 0.0) return vec3(0.0);
40+
if (!(dot(N, L) * dot(N, V) > 0.0))
41+
{
42+
pdf = 0;
43+
return vec3(0.0);
44+
}
4145

4246
float F = DielectricFresnel(dot(V, H), state.eta);
4347
float D = GTR2(dot(N, H), state.mat.roughness);
@@ -52,6 +56,12 @@ vec3 EvalDielectricReflection(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout
5256
vec3 EvalDielectricRefraction(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
5357
//-----------------------------------------------------------------------
5458
{
59+
if (dot(N, L) > 0.0)
60+
{
61+
pdf = 0;
62+
return vec3(0.0);
63+
}
64+
5565
float F = DielectricFresnel(abs(dot(V, H)), state.eta);
5666
float D = GTR2(dot(N, H), state.mat.roughness);
5767

@@ -66,23 +76,30 @@ vec3 EvalDielectricRefraction(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout
6676
vec3 EvalSpecular(State state, vec3 Cspec0, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
6777
//-----------------------------------------------------------------------
6878
{
69-
if (dot(N, L) < 0.0) return vec3(0.0);
79+
if (!(dot(N, L) * dot(N, V) > 0.0))
80+
{
81+
pdf = 0;
82+
return vec3(0.0);
83+
}
7084

71-
float D = GTR2_aniso(dot(N, H), dot(H, state.tangent), dot(H, state.bitangent), state.mat.ax, state.mat.ay);
85+
float D = GTR2(dot(N, H), state.mat.roughness);
7286
pdf = D * dot(N, H) / (4.0 * dot(V, H));
7387

7488
float FH = SchlickFresnel(dot(L, H));
7589
vec3 F = mix(Cspec0, vec3(1.0), FH);
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);
90+
float G = SmithG_GGX(abs(dot(N, L)), state.mat.roughness) * SmithG_GGX(abs(dot(N, V)), state.mat.roughness);
7891
return F * D * G;
7992
}
8093

8194
//-----------------------------------------------------------------------
8295
vec3 EvalClearcoat(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
8396
//-----------------------------------------------------------------------
8497
{
85-
if (dot(N, L) < 0.0) return vec3(0.0);
98+
if (!(dot(N, L) * dot(N, V) > 0.0))
99+
{
100+
pdf = 0;
101+
return vec3(0.0);
102+
}
86103

87104
float D = GTR1(dot(N, H), mix(0.1, 0.001, state.mat.clearcoatGloss));
88105
pdf = D * dot(N, H) / (4.0 * dot(V, H));
@@ -97,7 +114,11 @@ vec3 EvalClearcoat(State state, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
97114
vec3 EvalDiffuse(State state, vec3 Csheen, vec3 V, vec3 N, vec3 L, vec3 H, inout float pdf)
98115
//-----------------------------------------------------------------------
99116
{
100-
if (dot(N, L) < 0.0) return vec3(0.0);
117+
if (!(dot(N, L) * dot(N, V) > 0.0))
118+
{
119+
pdf = 0;
120+
return vec3(0.0);
121+
}
101122

102123
pdf = dot(N, L) * (1.0 / PI);
103124

@@ -108,8 +129,7 @@ vec3 EvalDiffuse(State state, vec3 Csheen, vec3 V, vec3 N, vec3 L, vec3 H, inout
108129
float Fd90 = 0.5 + 2.0 * dot(L, H) * dot(L, H) * state.mat.roughness;
109130
float Fd = mix(1.0, Fd90, FL) * mix(1.0, Fd90, FV);
110131

111-
// TODO: Replace with volumetric scattering
112-
// SS
132+
// Fake Subsurface TODO: Replace with volumetric scattering
113133
float Fss90 = dot(L, H) * dot(L, H) * state.mat.roughness;
114134
float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
115135
float ss = 1.25 * (Fss * (1.0 / (dot(N, L) + dot(N, V)) - 0.5) + 0.5);
@@ -122,7 +142,6 @@ vec3 EvalDiffuse(State state, vec3 Csheen, vec3 V, vec3 N, vec3 L, vec3 H, inout
122142
vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float pdf)
123143
//-----------------------------------------------------------------------
124144
{
125-
state.specularBounce = false;
126145
pdf = 0.0;
127146
vec3 f = vec3(0.0);
128147

@@ -140,13 +159,12 @@ vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float p
140159
vec3 Csheen = mix(vec3(1.0), Ctint, state.mat.sheenTint);
141160

142161
// TODO: Reuse random numbers and reduce so many calls to rand()
143-
// BSDF
144162
if (rand() < transWeight)
145163
{
146164
vec3 H = ImportanceSampleGTR2(state.mat.roughness, r1, r2);
147165
H = state.tangent * H.x + state.bitangent * H.y + N * H.z;
148166

149-
if (dot(V, H) < 0.0)
167+
if (dot(N, H) < 0.0)
150168
H = -H;
151169

152170
vec3 R = reflect(-V, H);
@@ -160,16 +178,14 @@ vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float p
160178
}
161179
else // Transmission
162180
{
163-
// TODO: Check how other renderers handle dielectrics
164-
state.specularBounce = true;
165181
L = normalize(refract(-V, H, state.eta));
166182
f = EvalDielectricRefraction(state, V, N, L, H, pdf);
167183
}
168184

169185
f *= transWeight;
170186
pdf *= transWeight;
171187
}
172-
else // BRDF
188+
else
173189
{
174190
if (rand() < diffuseRatio)
175191
{
@@ -189,10 +205,10 @@ vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float p
189205
if (rand() < primarySpecRatio)
190206
{
191207
// TODO: Implement http://jcgt.org/published/0007/04/01/
192-
vec3 H = ImportanceSampleGTR2_aniso(state.mat.ax, state.mat.ay, r1, r2);
208+
vec3 H = ImportanceSampleGTR2(state.mat.roughness, r1, r2);
193209
H = state.tangent * H.x + state.bitangent * H.y + N * H.z;
194210

195-
if (dot(V, H) < 0.0)
211+
if (dot(N, H) < 0.0)
196212
H = -H;
197213

198214
L = normalize(reflect(-V, H));
@@ -205,7 +221,7 @@ vec3 DisneySample(inout State state, vec3 V, vec3 N, inout vec3 L, inout float p
205221
vec3 H = ImportanceSampleGTR1(mix(0.1, 0.001, state.mat.clearcoatGloss), r1, r2);
206222
H = state.tangent * H.x + state.bitangent * H.y + N * H.z;
207223

208-
if (dot(V, H) < 0.0)
224+
if (dot(N, H) < 0.0)
209225
H = -H;
210226

211227
L = normalize(reflect(-V, H));
@@ -226,11 +242,12 @@ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, inout float pdf)
226242
//-----------------------------------------------------------------------
227243
{
228244
vec3 H;
245+
bool refl = dot(N, L) * dot(N, V) > 0.0;
229246

230-
if (dot(N, L) < 0.0)
231-
H = normalize(L * (1.0 / state.eta) + V);
232-
else
247+
if (refl)
233248
H = normalize(L + V);
249+
else
250+
H = normalize(L * (1.0 / state.eta) + V);
234251

235252
if (dot(N, H) < 0.0)
236253
H = -H;
@@ -244,17 +261,16 @@ vec3 DisneyEval(State state, vec3 V, vec3 N, vec3 L, inout float pdf)
244261
float brdfPdf = 0.0;
245262
float bsdfPdf = 0.0;
246263

247-
// BSDF
248264
if (transWeight > 0.0)
249265
{
250-
// Transmission
251-
if (dot(N, L) < 0.0)
266+
// Reflection
267+
if (refl)
252268
{
253-
bsdf = EvalDielectricRefraction(state, V, N, L, H, bsdfPdf);
269+
bsdf = EvalDielectricReflection(state, V, N, L, H, bsdfPdf);
254270
}
255-
else // Reflection
271+
else // Transmission
256272
{
257-
bsdf = EvalDielectricReflection(state, V, N, L, H, bsdfPdf);
273+
bsdf = EvalDielectricRefraction(state, V, N, L, H, bsdfPdf);
258274
}
259275
}
260276

src/shaders/common/globals.glsl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131
#define REFR 1
3232
#define SUBS 2
3333

34+
#define QUAD_LIGHT 0
35+
#define SPHERE_LIGHT 1
36+
3437
mat4 transform;
3538

3639
vec2 seed;

src/shaders/common/pathtrace.glsl

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ vec3 DirectLight(in Ray r, in State state)
142142
//-----------------------------------------------------------------------
143143
{
144144
vec3 Li = vec3(0.0);
145-
vec3 surfacePos = state.fhp + state.ffnormal * EPS;
145+
vec3 surfacePos = state.fhp + state.normal * EPS;
146146

147147
BsdfSampleRec bsdfSampleRec;
148148

@@ -155,21 +155,18 @@ vec3 DirectLight(in Ray r, in State state)
155155
vec3 lightDir = dirPdf.xyz;
156156
float lightPdf = dirPdf.w;
157157

158-
if (dot(lightDir, state.ffnormal) > 0.0)
158+
Ray shadowRay = Ray(surfacePos, lightDir);
159+
bool inShadow = AnyHit(shadowRay, INFINITY - EPS);
160+
161+
if (!inShadow)
159162
{
160-
Ray shadowRay = Ray(surfacePos, lightDir);
161-
bool inShadow = AnyHit(shadowRay, INFINITY - EPS);
163+
bsdfSampleRec.f = DisneyEval(state, -r.direction, state.ffnormal, lightDir, bsdfSampleRec.pdf);
162164

163-
if (!inShadow)
165+
if (bsdfSampleRec.pdf > 0.0)
164166
{
165-
bsdfSampleRec.f = DisneyEval(state, -r.direction, state.ffnormal, lightDir, bsdfSampleRec.pdf);
166-
167-
if (bsdfSampleRec.pdf > 0.0)
168-
{
169-
float misWeight = powerHeuristic(lightPdf, bsdfSampleRec.pdf);
170-
if (misWeight > 0.0)
171-
Li += misWeight * bsdfSampleRec.f * abs(dot(lightDir, state.ffnormal)) * color / lightPdf;
172-
}
167+
float misWeight = powerHeuristic(lightPdf, bsdfSampleRec.pdf);
168+
if (misWeight > 0.0)
169+
Li += misWeight * bsdfSampleRec.f * abs(dot(lightDir, state.ffnormal)) * color / lightPdf;
173170
}
174171
}
175172
}
@@ -203,7 +200,7 @@ vec3 DirectLight(in Ray r, in State state)
203200
float lightDistSq = lightDist * lightDist;
204201
lightDir /= sqrt(lightDistSq);
205202

206-
if (dot(lightDir, state.ffnormal) > 0.0 && dot(lightDir, lightSampleRec.normal) < 0.0)
203+
if (dot(lightDir, lightSampleRec.normal) < 0.0)
207204
{
208205
Ray shadowRay = Ray(surfacePos, lightDir);
209206
bool inShadow = AnyHit(shadowRay, lightDist - EPS);
@@ -234,6 +231,7 @@ vec3 PathTrace(Ray r)
234231
LightSampleRec lightSampleRec;
235232
BsdfSampleRec bsdfSampleRec;
236233
vec3 absorption = vec3(0.0);
234+
state.specularBounce = false;
237235

238236
for (int depth = 0; depth < maxDepth; depth++)
239237
{

src/shaders/common/sampling.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ void sampleRectLight(in Light light, inout LightSampleRec lightSampleRec)
215215
void sampleLight(in Light light, inout LightSampleRec lightSampleRec)
216216
//-----------------------------------------------------------------------
217217
{
218-
if (int(light.type) == 0) // Rect Light
218+
if (int(light.type) == QUAD_LIGHT)
219219
sampleRectLight(light, lightSampleRec);
220220
else
221221
sampleSphereLight(light, lightSampleRec);

validation/metallic_0.5.png

447 KB
Loading

validation/metallic_0.5_pbrt.png

274 KB
Loading

validation/metallic_1.0.png

446 KB
Loading

0 commit comments

Comments
 (0)