Skip to content

Commit db3cc01

Browse files
committed
merian-shaders/graph: improve specular / delta handling
- add delta lobe support - fix nan when the exit of an interface was missed (light bulbs on Bathroom) - Bump slang because there was a bug which surfaced with these changes; this made other fixes necessary because its caching mechanism changed.
1 parent 701d07d commit db3cc01

34 files changed

Lines changed: 511 additions & 182 deletions

include/merian-graph/nodes/gbuffer_rt/gbuffer_rt.slang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ void main(ParameterBlock<Scene> scene, ParameterBlock<WGBuffer> gbuffer) {
6161
}
6262

6363
const ShadingData sd = ShadingData(-ray.Direction, surface.pos, surface.uv, scene.time,
64-
frame, face_normal, uint16_t(0), 1.0f, front_facing);
64+
frame, face_normal, uint16_t(0), front_facing);
6565
float3 mat_emission;
6666
OrthonormalFrame mat_shading_frame;
6767
let mat = scene.material_system.get_material_sample(

include/merian-graph/nodes/render_pt/render_pt.slang

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ float3 trace_path(const ParameterBlock<Scene> scene,
2727
inout RandomGenerator rng) {
2828
// Medium the ray is travelling through; updated on transmission events.
2929
HomogeneousVolume medium = scene.exterior_volume;
30-
float current_ior = 1.0f;
3130

3231
float3 throughput = medium.transmittance(distance(ray.Origin, sp.pos));
3332
float3 radiance = merian_render_emission_on_primary ? throughput * sp.emission : float3(0);
@@ -60,7 +59,6 @@ float3 trace_path(const ParameterBlock<Scene> scene,
6059
const bool transmission = wi_local.z * bs.wo.z < 0.f;
6160
if (transmission) {
6261
const IsotropicVolume interior = sp.surface_material.get_interior_volume();
63-
current_ior /= interior.get_eta();
6462
// leaving a surface puts the ray back into the medium enclosing the scene
6563
if (dot(-ray.Direction, sp.get_face_normal()) >= 0.f)
6664
medium = interior;
@@ -76,7 +74,7 @@ float3 trace_path(const ParameterBlock<Scene> scene,
7674

7775
// the last vertex is never scattered from, so its material sample would be dead
7876
sp = scene.trace_and_get_shading_point(
79-
ray, merian_render_instance_mask, RenderContext(uint16_t(bounce), current_ior),
77+
ray, merian_render_instance_mask, RenderContext(uint16_t(bounce)),
8078
DefaultLODSampler(), bounce + 1 < merian_render_max_path_length);
8179

8280
// Beer-Lambert attenuation over the segment just travelled inside the medium. In-scattering

include/merian-graph/nodes/render_pt_mcpg/render_pt_mcpg.slang

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ float3 trace_path(const ParameterBlock<Scene> scene,
5050

5151
// Medium the ray is travelling through; updated on transmission events.
5252
HomogeneousVolume medium = scene.exterior_volume;
53-
float current_ior = 1.0f;
5453

5554
float3 throughput = medium.transmittance(distance(ray.Origin, sp.pos));
5655
float3 radiance = merian_render_emission_on_primary ? throughput * sp.emission : float3(0);
@@ -112,14 +111,19 @@ float3 trace_path(const ParameterBlock<Scene> scene,
112111

113112
// Sample wo from the BSDF or the selected lobe
114113
const bool guide = can_guide && rng.next_float() < p_guide;
114+
const float bsdf_w = can_guide ? (1.0 - p_guide) : 1.0;
115115
float3 wo_world;
116116
float3 wo_local;
117+
bool delta = false;
118+
float3 delta_weight = float3(0);
117119
if (guide) {
118120
wo_world = vmfs[0].sample(rng);
119121
wo_local = frame.to_local(wo_world);
120-
} else if (let local = m_bsdf.sample(wi_local, rng)) {
121-
wo_local = local;
122-
wo_world = frame.to_world(local);
122+
} else if (let s = m_bsdf.sample_eval(wi_local, rng)) {
123+
wo_local = s.wo;
124+
wo_world = frame.to_world(s.wo);
125+
delta = s.lobe == BSDFLobe::Delta;
126+
delta_weight = s.weight;
123127
selected = MCState();
124128
selected_pos = HashGridPos();
125129
selected_level = params.mcpg.grid.target_level_for_pos(cur_pos, cam_pos, rng);
@@ -128,17 +132,24 @@ float3 trace_path(const ParameterBlock<Scene> scene,
128132
}
129133

130134
// Stochastic MIS
131-
float wo_p = 0.0;
132-
if (can_guide) {
133-
for (int i = 0; i < mc_samples; i++) {
134-
wo_p += scores[i] * vmfs[i].pdf(wo_world);
135+
float wo_p;
136+
float3 bsdf;
137+
if (delta) {
138+
// the guiding mixture cannot produce a delta direction, only the BSDF strategy can
139+
wo_p = bsdf_w;
140+
bsdf = bsdf_w * delta_weight;
141+
} else {
142+
float guide_p = 0.0;
143+
if (can_guide) {
144+
for (int i = 0; i < mc_samples; i++) {
145+
guide_p += scores[i] * vmfs[i].pdf(wo_world);
146+
}
147+
guide_p /= score_sum;
135148
}
136-
wo_p /= score_sum;
149+
wo_p = bsdf_w * m_bsdf.pdf(wi_local, wo_local) +
150+
select(can_guide, p_guide, 0.0) * guide_p;
151+
bsdf = m_bsdf.eval(wi_local, wo_local, rng);
137152
}
138-
const float bsdf_w = can_guide ? (1.0 - p_guide) : 1.0;
139-
wo_p = bsdf_w * m_bsdf.pdf(wi_local, wo_local) + select(can_guide, p_guide, 0.0) * wo_p;
140-
141-
const float3 bsdf = m_bsdf.eval(wi_local, wo_local, rng);
142153
const bool transmission = wi_local.z * wo_local.z < 0.f;
143154
Optional<RayDesc> next_ray = none;
144155
if (any(bsdf > 0.0))
@@ -151,7 +162,6 @@ float3 trace_path(const ParameterBlock<Scene> scene,
151162
if (let next = next_ray) {
152163
if (transmission) {
153164
const IsotropicVolume interior = sp.surface_material.get_interior_volume();
154-
current_ior /= interior.get_eta();
155165
// leaving a surface puts the ray back into the medium enclosing the scene
156166
if (dot(-ray.Direction, sp.get_face_normal()) >= 0.f)
157167
medium = interior;
@@ -161,7 +171,7 @@ float3 trace_path(const ParameterBlock<Scene> scene,
161171
ray = next;
162172
// the last vertex is never scattered from, so its material sample would be dead
163173
sp = scene.trace_and_get_shading_point(
164-
ray, merian_render_instance_mask, RenderContext(bounce, current_ior),
174+
ray, merian_render_instance_mask, RenderContext(bounce),
165175
DefaultLODSampler(), bounce + 1 < merian_render_max_path_length);
166176
next_pos = sp.pos;
167177

include/merian-graph/nodes/render_pt_mcpg/volume.slang

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ void render_pixel(const uint2 pixel,
170170
ray.TMax = SCENE_RAY_TMAX;
171171
// single scattering never scatters off this surface, so the material sample would be dead
172172
const ShadingPoint sp = scene.trace_and_get_shading_point(
173-
ray, merian_render_instance_mask, RenderContext(uint16_t(1), 1.0f),
173+
ray, merian_render_instance_mask, RenderContext(uint16_t(1)),
174174
DefaultLODSampler(), false);
175175

176176
float3 incident = sp.emission;

include/merian-graph/nodes/render_ssmm/render_ssmm.slang

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ void main(ParameterBlock<Scene> scene, ParameterBlock<SSMMBinding> params) {
146146

147147
float3 sample_dirs[SSMM_MAX_SPP];
148148
float3 sample_weights[SSMM_MAX_SPP];
149+
bool sample_delta[SSMM_MAX_SPP];
149150
float4 vmfs[SSMM_MAX_SPP];
150151

151152
mc_state_t curr = mc_state_new(); // zero current state
@@ -170,6 +171,7 @@ void main(ParameterBlock<Scene> scene, ParameterBlock<SSMMBinding> params) {
170171
mc_state_shuffle(tent, (WaveGetLaneIndex() + 1) % WaveGetLaneCount());
171172
sample_dirs[s] = float3(0);
172173
sample_weights[s] = float3(0);
174+
sample_delta[s] = false;
173175

174176
read_neighbour_state(params.gbuffer, scene.camera.position, resolution, tent,
175177
sp.pos, normal, int2(pixel), mv, rng);
@@ -183,24 +185,29 @@ void main(ParameterBlock<Scene> scene, ParameterBlock<SSMMBinding> params) {
183185
float3 direction = float3(0);
184186
float3 position = float3(0);
185187
bool bsdf_sample = false;
188+
float3 throughput = float3(0);
186189
do {
187190
if (vmfs[s].w == 0 || rng.next_float() < merian_ssmm_bsdf_p) {
188-
let wo_local = bsdf.sample(wi_local, rng);
189-
if (!wo_local.hasValue)
191+
let bs_opt = bsdf.sample_eval(wi_local, rng);
192+
if (!bs_opt.hasValue)
190193
break;
191-
direction = frame.to_world(wo_local.value);
194+
const ShadingFunctionSample bs = bs_opt.value;
195+
direction = frame.to_world(bs.wo);
192196
if (dot(direction, normal) <= 1e-3 || dot(direction, geonormal) <= 1e-3)
193197
break;
194198

195-
pdf = bsdf.pdf(wi_local, wo_local.value);
199+
sample_delta[s] = bs.lobe == BSDFLobe::Delta;
200+
// a delta weight is already f / p
201+
pdf = sample_delta[s] ? 1.0 : bs.pdf;
202+
throughput =
203+
sample_delta[s] ? bs.weight : bsdf.eval(wi_local, bs.wo, rng);
196204
bsdf_sample = true;
197205
} else {
198206
direction = vmf_sample_tokuyosh(vmfs[s].xyz, vmfs[s].w, rng.next_float2());
199207
pdf = vmf_pdf_tokuyoshi(direction, vmfs[s].xyz, vmfs[s].w);
208+
throughput = bsdf.eval(wi_local, frame.to_local(direction), rng);
200209
}
201210

202-
const float3 throughput = bsdf.eval(wi_local, frame.to_local(direction), rng);
203-
204211
RayDesc ray;
205212
// Pull back the ray such that it cannot excape through corners (and to
206213
// prevent self collision)
@@ -209,7 +216,7 @@ void main(ParameterBlock<Scene> scene, ParameterBlock<SSMMBinding> params) {
209216
ray.TMin = SCENE_RAY_TMIN;
210217
ray.TMax = SCENE_RAY_TMAX;
211218
const ShadingPoint next_sp = scene.trace_and_get_shading_point(
212-
ray, 0xffu, RenderContext(uint16_t(1), 1.0f));
219+
ray, 0xffu, RenderContext(uint16_t(1)));
213220

214221
position = next_sp.pos;
215222
directContrib = throughput * next_sp.emission / pdf;
@@ -236,16 +243,22 @@ void main(ParameterBlock<Scene> scene, ParameterBlock<SSMMBinding> params) {
236243
if (all(sample_weights[s] == float3(0)))
237244
continue;
238245

239-
const float bsdf_pdf = bsdf.pdf(wi_local, frame.to_local(sample_dirs[s]));
240-
float sum_pdf = 0;
241-
for (int t = 0; t < merian_ssmm_spp; t++) {
242-
sum_pdf += vmfs[t].w > 0
243-
? vmf_pdf_tokuyoshi(sample_dirs[s], vmfs[t].xyz, vmfs[t].w)
244-
: bsdf_pdf;
245-
}
246+
float sum_pdf;
247+
if (sample_delta[s]) {
248+
// only the BSDF strategy, shared by the whole group, reaches a delta direction
249+
sum_pdf = merian_ssmm_bsdf_p * merian_ssmm_smis_group_size;
250+
} else {
251+
const float bsdf_pdf = bsdf.pdf(wi_local, frame.to_local(sample_dirs[s]));
252+
sum_pdf = 0;
253+
for (int t = 0; t < merian_ssmm_spp; t++) {
254+
sum_pdf += vmfs[t].w > 0
255+
? vmf_pdf_tokuyoshi(sample_dirs[s], vmfs[t].xyz, vmfs[t].w)
256+
: bsdf_pdf;
257+
}
246258

247-
sum_pdf = merian_ssmm_bsdf_p * merian_ssmm_smis_group_size * bsdf_pdf +
248-
(1 - merian_ssmm_bsdf_p) * sum_pdf;
259+
sum_pdf = merian_ssmm_bsdf_p * merian_ssmm_smis_group_size * bsdf_pdf +
260+
(1 - merian_ssmm_bsdf_p) * sum_pdf;
261+
}
249262

250263
if (sum_pdf > 0) {
251264
const float3 con = sample_weights[s] / sum_pdf;

include/merian-graph/nodes/shadertoy/shadertoy.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class Shadertoy : public AbstractCompute {
5959
// Validates the composed shader; stores the error and returns false on failure.
6060
bool try_compile(const std::string& body);
6161

62+
// Unique per instance: several Shadertoy nodes coexist, and a module name binds to one source.
63+
const std::string module_name;
64+
6265
int shader_source_selector = 0;
6366
std::string shader_glsl;
6467
std::string shader_path = {0};

include/merian-shaders/scene/scene.slang

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ struct Scene {
438438
}
439439

440440
const ShadingData sd = ShadingData(-ray.Direction, surface.pos, surface.uv, time, frame,
441-
face_normal, ctx.bounce, ctx.ior, front_facing);
441+
face_normal, ctx.bounce, front_facing);
442442

443443
ShadingPoint sp;
444444
sp.pos = surface.pos;
@@ -485,7 +485,7 @@ struct Scene {
485485
const float3 pos = ray.Origin + ray.Direction * ENV_MAP_DISTANCE;
486486
const ShadingData sd =
487487
ShadingData(-ray.Direction, pos, float2(0), time, OrthonormalFrame(-ray.Direction),
488-
-ray.Direction, ctx.bounce, ctx.ior, true);
488+
-ray.Direction, ctx.bounce, true);
489489

490490
ShadingPoint sp;
491491
sp.pos = pos;

0 commit comments

Comments
 (0)