Skip to content

Commit db903d6

Browse files
committed
super simple: medium tracking, HG volumetric scattering and test render under render-mx-medium-vdf
1 parent 233a54a commit db903d6

14 files changed

Lines changed: 624 additions & 45 deletions

File tree

src/cmake/testing.cmake

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,8 @@ macro (osl_add_all_tests)
371371
render-mx-generalized-schlick render-mx-generalized-schlick-glass
372372
render-mx-layer
373373
render-mx-sheen
374-
render-microfacet render-oren-nayar
374+
render-mx-medium-vdf
375+
render-microfacet render-oren-nayar
375376
render-spi-thinlayer
376377
render-uv render-veachmis render-ward
377378
render-raytypes

src/testrender/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# The 'testrender' executable
66
set (testrender_srcs
77
shading.cpp
8+
volume.cpp
89
simpleraytracer.cpp
910
scene.cpp
1011
bvh.cpp

src/testrender/shading.cpp

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,8 @@ struct MxDielectric : public bsdl::mtx::DielectricLobe<BSDLLobe> {
167167
}
168168
};
169169

170+
171+
170172
#ifndef __CUDACC__
171173
// Helper to register BSDL closures
172174
struct BSDLtoOSL {
@@ -1649,36 +1651,40 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16491651
const ClosureComponent* comp = closure->as_comp();
16501652
Color3 cw = weight * comp->w;
16511653
const auto& params = *comp->as<MxAnisotropicVdfParams>();
1652-
result.sigma_t = cw * params.extinction;
1653-
result.sigma_s = params.albedo * result.sigma_t;
1654-
result.medium_g = params.anisotropy;
1654+
result.medium_data.sigma_t = cw * params.extinction;
1655+
result.medium_data.sigma_s = params.albedo * result.medium_data.sigma_t;
1656+
result.medium_data.medium_g = params.anisotropy;
16551657
closure = nullptr;
16561658
break;
16571659
}
16581660
case MX_MEDIUM_VDF_ID: {
16591661
const ClosureComponent* comp = closure->as_comp();
16601662
Color3 cw = weight * comp->w;
16611663
const auto& params = *comp->as<MxMediumVdfParams>();
1662-
result.sigma_t = { -OIIO::fast_log(params.transmission_color.x),
1663-
-OIIO::fast_log(params.transmission_color.y),
1664-
-OIIO::fast_log(params.transmission_color.z) };
1665-
// NOTE: closure weight scales the extinction parameter
1666-
result.sigma_t *= cw / params.transmission_depth;
1667-
result.sigma_s = params.albedo * result.sigma_t;
1668-
result.medium_g = params.anisotropy;
1669-
// TODO: properly track a medium stack here ...
1670-
result.refraction_ior = sg.backfacing ? 1.0f / params.ior
1671-
: params.ior;
1672-
result.priority = params.priority;
1673-
closure = nullptr;
1664+
1665+
result.medium_data.sigma_t = Color3(
1666+
-OIIO::fast_log(params.transmission_color.x),
1667+
-OIIO::fast_log(params.transmission_color.y),
1668+
-OIIO::fast_log(params.transmission_color.z)
1669+
);
1670+
1671+
result.medium_data.sigma_t *= cw / params.transmission_depth;
1672+
result.medium_data.sigma_s = params.albedo * result.medium_data.sigma_t;
1673+
result.medium_data.medium_g = params.anisotropy;
1674+
1675+
// Track IOR and priority here
1676+
result.medium_data.refraction_ior = sg.backfacing ? 1.0f / params.ior
1677+
: params.ior;
1678+
result.medium_data.priority = params.priority;
1679+
closure = nullptr;
16741680
break;
16751681
}
16761682
case MxDielectric::closureid(): {
16771683
const ClosureComponent* comp = closure->as_comp();
16781684
const MxDielectric::Data& params = *comp->as<MxDielectric::Data>();
16791685
if (!is_black(weight * comp->w * params.refr_tint)) {
16801686
// TODO: properly track a medium stack here ...
1681-
result.refraction_ior = sg.backfacing ? 1.0f / params.IOR
1687+
result.medium_data.refraction_ior = sg.backfacing ? 1.0f / params.IOR
16821688
: params.IOR;
16831689
}
16841690
closure = nullptr;
@@ -1694,7 +1700,7 @@ process_medium_closure(const ShaderGlobalsType& sg, float path_roughness,
16941700
0.0f, 0.99f);
16951701
float sqrt_F0 = sqrtf(avg_F0);
16961702
float ior = (1 + sqrt_F0) / (1 - sqrt_F0);
1697-
result.refraction_ior = sg.backfacing ? 1.0f / ior : ior;
1703+
result.medium_data.refraction_ior = sg.backfacing ? 1.0f / ior : ior;
16981704
}
16991705
closure = nullptr;
17001706
break;
@@ -1868,7 +1874,7 @@ process_bsdf_closure(const ShaderGlobalsType& sg, float path_roughness,
18681874
else
18691875
ok = result.bsdf.add_bsdf<MxMicrofacet<
18701876
MxGeneralizedSchlickParams, GGXDist, true>>(
1871-
cw, params, result.refraction_ior);
1877+
cw, params, result.medium_data.refraction_ior);
18721878
break;
18731879
};
18741880
case MX_TRANSLUCENT_ID: {

src/testrender/shading.h

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include "optics.h"
1818
#include "sampling.h"
19+
#include "volume.h"
1920

2021

2122
OSL_NAMESPACE_BEGIN
@@ -454,15 +455,13 @@ struct CompositeBSDF {
454455
int num_bsdfs, num_bytes;
455456
};
456457

458+
struct MediumProperties;
459+
460+
457461
struct ShadingResult {
458462
Color3 Le = Color3(0.0f);
459463
CompositeBSDF bsdf = {};
460-
// medium data
461-
Color3 sigma_s = Color3(0.0f);
462-
Color3 sigma_t = Color3(0.0f);
463-
float medium_g = 0.0f; // volumetric anisotropy
464-
float refraction_ior = 1.0f;
465-
int priority = 0;
464+
MediumProperties medium_data = {};
466465
};
467466

468467
void

src/testrender/simpleraytracer.cpp

Lines changed: 82 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ namespace pugi = OIIO::pugi;
1717
# include "raytracer.h"
1818
# include "shading.h"
1919
# include "simpleraytracer.h"
20+
# include "volume.h"
2021
#endif
2122

2223
#include <cmath>
@@ -968,11 +969,66 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
968969
int prev_id = -1;
969970
float bsdf_pdf = inf; // camera ray has only one possible direction
970971

972+
MediumStack medium_stack;
973+
971974
for (int b = 0; b <= max_bounces; b++) {
972975
ShaderGlobalsType sg;
973-
974976
// trace the ray against the scene
975977
Intersection hit = scene.intersect(r, inf, prev_id);
978+
979+
const MediumProperties* current_volume = medium_stack.current();
980+
bool in_medium = medium_stack.in_medium();
981+
982+
//determine whether ray is in a medium
983+
float t_volume = inf;
984+
if (in_medium && current_volume && !current_volume->is_vacuum()) {
985+
Vec3 rand_vol = sampler.get();
986+
t_volume = current_volume->sample_distance(rand_vol.x);
987+
}
988+
989+
//is there volume scattering
990+
bool volume_scatter = in_medium && (t_volume < hit.t);
991+
float t_event = volume_scatter ? t_volume : hit.t;
992+
993+
//apply volumetric transmittance up to the event
994+
if (in_medium && current_volume && !current_volume->is_vacuum()) {
995+
Color3 transmittance = current_volume->transmittance(t_event);
996+
path_weight *= transmittance;
997+
998+
//early exit if transmittance kills the path
999+
if (!(path_weight.x > 0) && !(path_weight.y > 0) && !(path_weight.z > 0)) {
1000+
break;
1001+
}
1002+
}
1003+
1004+
// do volume scattering
1005+
if (volume_scatter) {
1006+
r.origin = r.origin + r.direction * t_volume;
1007+
1008+
Vec3 rand_phase = sampler.get();
1009+
HenyeyGreenstein phase_func(current_volume->medium_g);
1010+
//IsotropicPhase phase_func;
1011+
PhaseFunction::Sample phase_sample = phase_func.sample(-r.direction, rand_phase.x, rand_phase.y);
1012+
1013+
if (phase_sample.pdf <= 0.0f) {
1014+
break;
1015+
}
1016+
1017+
//single scattering albedo = sigma_s / sigma_t
1018+
Color3 albedo = Color3(
1019+
current_volume->sigma_s.x / current_volume->sigma_t.x,
1020+
current_volume->sigma_s.y / current_volume->sigma_t.y,
1021+
current_volume->sigma_s.z / current_volume->sigma_t.z
1022+
);
1023+
1024+
path_weight *= albedo * phase_sample.weight;
1025+
1026+
r.direction = phase_sample.wi;
1027+
bsdf_pdf = phase_sample.pdf;
1028+
1029+
continue;
1030+
}
1031+
9761032
if (hit.t == inf) {
9771033
// we hit nothing? check background shader
9781034
if (backgroundShaderID >= 0) {
@@ -1018,8 +1074,9 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
10181074
int shaderID = scene.shaderid(hit.id);
10191075

10201076
#ifndef __CUDACC__
1021-
if (shaderID < 0 || !m_shaders[shaderID].surf)
1077+
if (shaderID < 0 || !m_shaders[shaderID].surf) {
10221078
break; // no shader attached? done
1079+
}
10231080

10241081
// execute shader and process the resulting list of closures
10251082
shadingsys->execute(*ctx, *m_shaders[shaderID].surf, sg);
@@ -1032,7 +1089,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
10321089
bool last_bounce = b == max_bounces;
10331090
process_closure(sg, r.roughness, result, (const ClosureColor*)sg.Ci,
10341091
last_bounce);
1035-
1092+
10361093
#ifndef __CUDACC__
10371094
const size_t lightprims_size = m_lightprims.size();
10381095
#endif
@@ -1076,16 +1133,17 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
10761133
float bg_pdf = 0;
10771134
Vec3 bg = background.sample(xi, yi, bg_dir, bg_pdf);
10781135
BSDF::Sample b = result.bsdf.eval(-sg.I, bg_dir.val());
1079-
Color3 contrib = path_weight * b.weight * bg
1080-
* MIS::power_heuristic<MIS::WEIGHT_WEIGHT>(bg_pdf,
1136+
Color3 contrib = path_weight * b.weight *
1137+
bg * MIS::power_heuristic<MIS::WEIGHT_WEIGHT>(bg_pdf,
10811138
b.pdf);
10821139
if ((contrib.x + contrib.y + contrib.z) > 0) {
10831140
ShaderGlobalsType shadow_sg;
10841141
Ray shadow_ray = Ray(sg.P, bg_dir.val(), radius, 0, 0,
10851142
Ray::SHADOW);
10861143
Intersection shadow_hit = scene.intersect(shadow_ray, inf,
1087-
hit.id);
1088-
if (shadow_hit.t == inf) // ray reached the background?
1144+
hit.id);
1145+
1146+
if (shadow_hit.t == inf) // ray reached the background?
10891147
path_radiance += contrib;
10901148
}
10911149
}
@@ -1114,27 +1172,21 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
11141172
Ray shadow_ray = Ray(sg.P, sample.dir, radius, 0, 0,
11151173
Ray::SHADOW);
11161174
// trace a shadow ray and see if we actually hit the target
1117-
// in this tiny renderer, tracing a ray is probably cheaper than evaluating the light shader
11181175
Intersection shadow_hit
11191176
= scene.intersect(shadow_ray, sample.dist, hit.id, lid);
11201177

11211178
#ifndef __CUDACC__
11221179
const bool did_hit = shadow_hit.t == sample.dist;
11231180
#else
1124-
// The hit distance on the device is not as precise as on
1125-
// the CPU, so we need to allow a little wiggle room. An
1126-
// epsilon of 1e-3f empirically gives results that closely
1127-
// match the CPU for the test scenes, so that's what we're
1128-
// using.
11291181
const bool did_hit = fabsf(shadow_hit.t - sample.dist)
11301182
< 1e-3f;
1131-
#endif
1183+
#endif
1184+
11321185
if (did_hit) {
1133-
// setup a shader global for the point on the light
1186+
11341187
globals_from_hit(light_sg, shadow_ray, sample.dist, lid,
11351188
sample.u, sample.v);
11361189
#ifndef __CUDACC__
1137-
// execute the light shader (for emissive closures only)
11381190
shadingsys->execute(*ctx, *m_shaders[shaderID].surf,
11391191
light_sg);
11401192
#else
@@ -1143,7 +1195,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
11431195
ShadingResult light_result;
11441196
process_closure(light_sg, r.roughness, light_result,
11451197
(const ClosureColor*)light_sg.Ci, true);
1146-
// accumulate contribution
1198+
11471199
path_radiance += contrib * light_result.Le;
11481200
}
11491201
}
@@ -1160,12 +1212,22 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
11601212
// Just simply use roughness as spread slope
11611213
r.spread = std::max(r.spread, p.roughness);
11621214
r.roughness = p.roughness;
1163-
if (!(path_weight.x > 0) && !(path_weight.y > 0)
1164-
&& !(path_weight.z > 0))
1215+
1216+
if (!result.medium_data.is_vacuum()) {
1217+
bool entering = !sg.backfacing;
1218+
1219+
if (entering) {
1220+
medium_stack.push(result.medium_data);
1221+
} else {
1222+
medium_stack.pop();
1223+
}
1224+
}
1225+
1226+
if (!(path_weight.x > 0) && !(path_weight.y > 0) && !(path_weight.z > 0) && b > 10)
11651227
break; // filter out all 0's or NaNs
11661228
prev_id = hit.id;
11671229
r.origin = sg.P;
1168-
}
1230+
}
11691231
return path_radiance;
11701232
}
11711233

src/testrender/volume.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "volume.h"
2+
#include "sampling.h"
3+
4+
5+
OSL_NAMESPACE_BEGIN
6+
7+
8+
OSL_NAMESPACE_END

0 commit comments

Comments
 (0)