Skip to content

Commit

Permalink
Set up ray types for testrender (AcademySoftwareFoundation#1648)
Browse files Browse the repository at this point in the history
Try to set ray types correctly for rays -- a little naive now, only
using "camera", "shadow", and "diffuse" for all rays (leave
distinguishing between reflection, refraction, diffuse, glossy for
another day, and currently testrender doesn't support subsurface or
displacement, but maybe someday).

Also, for testshade, improve efficency -- don't decode raytype on
every point.  For each individual shade, testshade was converting the
name of the raytype from a string to a ustring, then decoding to a
bitfield. Pull it all out of the loops by computing the bitfield once.

Adjust glass shaders to not change behavior based on raytype:
The code path that was supposed to suppress noisy caustic rays never
worked properly before because we never set the ray type in
testrender. Now that testrender sets it -- but does not distinguish
between the different types of secondary rays -- the code path is
active, but incorrect, and causes tests to fail. Just remove the
offending clause for now and return to the old behavior of the shader
(no attempt to eliminate caustic rays). Later, we should revamp the
glass shader entirely by switching to the newer MaterialX-inspired
closures.

Note: when you specify a resolution in the Background command in our 
scene xml, it will  run the shader out of any ray context at all (therefore
having no raytype) in order to populate an environment map that will be
used as needed. If you omit the resolution, no importance sampling is
used at all, so it's much noisier. I think the right approach is to do both --
populate the map for importance sampling, but run the shader on the rays
themselves to get higher quality visible results.

Signed-off-by: Larry Gritz <lg@larrygritz.com>
  • Loading branch information
lgritz committed Mar 1, 2023
1 parent c14bcd2 commit a5777fd
Show file tree
Hide file tree
Showing 15 changed files with 212 additions and 68 deletions.
8 changes: 5 additions & 3 deletions src/cmake/testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ macro (add_one_testsuite testname testsrcdir)
set_tests_properties (${testname} PROPERTIES LABELS render
PROCESSORS 4 COST 10 TIMEOUT ${OSL_TEST_BIG_TIMEOUT})
endif ()
# For debug builds, render tests are very slow, so give them a longer timeout
if (${testname} MATCHES "render-" AND ${CMAKE_BUILD_TYPE} STREQUAL Debug)
set_tests_properties (${testname} PROPERTIES TIMEOUT ${OSL_TEST_BIG_TIMEOUT})
endif ()
# Some labeling for fun
if (${testname} MATCHES "^texture")
set_tests_properties (${testname} PROPERTIES LABELS texture
Expand Down Expand Up @@ -139,9 +143,6 @@ macro ( TESTSUITE )
add_one_testsuite ("${_testname}.opt" "${_testsrcdir}"
ENV TESTSHADE_OPT=2 )
endif ()
if (_testname MATCHES "render-" AND ${CMAKE_BUILD_TYPE} STREQUAL Debug)
set_tests_properties (${_testname} PROPERTIES TIMEOUT ${OSL_TEST_BIG_TIMEOUT})
endif ()
# When building for OptiX support, also run it in OptiX mode
# if there is an OPTIX marker file in the directory.
# If an environment variable $TESTSUITE_OPTIX is nonzero, then
Expand Down Expand Up @@ -323,6 +324,7 @@ macro (osl_add_all_tests)
render-mx-sheen
render-microfacet render-oren-nayar
render-uv render-veachmis render-ward
render-raytypes
select select-reg shaderglobals shortcircuit
smoothstep-reg
spline spline-reg splineinverse splineinverse-ident
Expand Down
30 changes: 10 additions & 20 deletions src/shaders/glass.osl
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,20 @@ glass
float eta = 1.5
[[ string help = "Index of refraction",
float min = 1, float max = 3 ]],
int caustics = 0
[[ string help = "Enable indirect lighting through glass",
string widget = "checkBox" ]],
int TIR = 0
[[ string help = "Enable Total Internal Reflection",
string widget = "checkBox" ]]
)
{
// If the current ray type is glossy or diffuse, that means we
// are computing indirect lighting. Letting that interact with
// glass produces caustics, which are normally hard to trace and
// noisy. So we only return closures if that's not the case or
// the "caustics" flag is on.
if (caustics || (!raytype("glossy") && !raytype("diffuse"))) {
// Take into account backfacing to invert eta accordingly
if (backfacing()) {
Ci = Cs * refraction(N, 1.0 / eta);
// If Total Internal Reflection is enabled, we also return a
// reflection closure, which might make rays bounce too much
// inside an object. That's why we make it optional.
if (TIR)
Ci += Ks * reflection(N, 1.0 / eta);
} else {
Ci = Ks * reflection(N, eta) + Cs * refraction(N, eta);
}
// Take into account backfacing to invert eta accordingly
if (backfacing()) {
Ci = Cs * refraction(N, 1.0 / eta);
// If Total Internal Reflection is enabled, we also return a
// reflection closure, which might make rays bounce too much
// inside an object. That's why we make it optional.
if (TIR)
Ci += Ks * reflection(N, 1.0 / eta);
} else {
Ci = Ks * reflection(N, eta) + Cs * refraction(N, eta);
}
}
23 changes: 20 additions & 3 deletions src/testrender/raytracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,24 @@ ortho(const Vec3& n, Vec3& x, Vec3& y)

// Note: not used in OptiX mode
struct Ray {
Ray(const Vec3& o, const Vec3& d, float radius, float spread)
: origin(o), direction(d), radius(radius), spread(spread)
enum RayType {
CAMERA = 1,
SHADOW = 2,
REFLECTION = 4,
REFRACTION = 8,
DIFFUSE = 16,
GLOSSY = 32,
SUBSURFACE = 64,
DISPLACEMENT = 128
};

Ray(const Vec3& o, const Vec3& d, float radius, float spread,
RayType raytype)
: origin(o)
, direction(d)
, radius(radius)
, spread(spread)
, raytype(static_cast<int>(raytype))
{
}

Expand Down Expand Up @@ -81,6 +97,7 @@ struct Ray {

Vec3 origin, direction;
float radius, spread;
int raytype;
};


Expand Down Expand Up @@ -125,7 +142,7 @@ struct Camera {
const float cos_a = dir.dot(v);
const float spread
= sqrtf(invw * invh * cx.length() * cy.length() * cos_a) * cos_a;
return Ray(eye, v, 0, spread);
return Ray(eye, v, 0, spread, Ray::CAMERA);
}

// Specified by user:
Expand Down
16 changes: 11 additions & 5 deletions src/testrender/simpleraytracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ SimpleRaytracer::globals_from_hit(ShaderGlobals& sg, const Ray& r,
sg.N = -sg.N;
sg.Ng = -sg.Ng;
}
sg.raytype = r.raytype;
sg.flipHandedness = sg.dPdx.cross(sg.dPdy).dot(sg.N) < 0;

// In our SimpleRaytracer, the "renderstate" itself just a pointer to
Expand All @@ -831,13 +832,16 @@ SimpleRaytracer::globals_from_hit(ShaderGlobals& sg, const Ray& r,
}

Vec3
SimpleRaytracer::eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx)
SimpleRaytracer::eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx,
int bounce)
{
ShaderGlobals sg;
memset((char*)&sg, 0, sizeof(ShaderGlobals));
sg.I = dir.val();
sg.dIdx = dir.dx();
sg.dIdy = dir.dy();
if (bounce >= 0)
sg.raytype = bounce > 0 ? Ray::DIFFUSE : Ray::CAMERA;
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID], sg);
return process_background_closure(sg.Ci);
}
Expand Down Expand Up @@ -869,7 +873,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
} else {
// we aren't importance sampling the background - so just run it directly
path_radiance += path_weight
* eval_background(r.direction, ctx);
* eval_background(r.direction, ctx, b);
}
}
break;
Expand Down Expand Up @@ -931,7 +935,8 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
b.pdf);
if ((contrib.x + contrib.y + contrib.z) > 0) {
int shadow_id = id;
Ray shadow_ray = Ray(sg.P, bg_dir.val(), radius, 0);
Ray shadow_ray = Ray(sg.P, bg_dir.val(), radius, 0,
Ray::SHADOW);
Dual2<float> shadow_dist;
if (!scene.intersect(shadow_ray, shadow_dist,
shadow_id)) // ray reached the background?
Expand All @@ -956,7 +961,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
* MIS::power_heuristic<MIS::EVAL_WEIGHT>(light_pdf,
b.pdf);
if ((contrib.x + contrib.y + contrib.z) > 0) {
Ray shadow_ray = Ray(sg.P, ldir, radius, 0);
Ray shadow_ray = Ray(sg.P, ldir, radius, 0, Ray::SHADOW);
// trace a shadow ray and see if we actually hit the target
// in this tiny renderer, tracing a ray is probably cheaper than evaluating the light shader
int shadow_id = id; // ignore self hit
Expand All @@ -979,7 +984,8 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
// trace indirect ray and continue
BSDF::Sample p = result.bsdf.sample(-sg.I, xi, yi, zi);
path_weight *= p.weight;
bsdf_pdf = p.pdf;
bsdf_pdf = p.pdf;
r.raytype = Ray::DIFFUSE; // FIXME? Use DIFFUSE for all indiirect rays
r.direction = p.wi;
r.radius = radius;
// Just simply use roughness as spread slope
Expand Down
3 changes: 2 additions & 1 deletion src/testrender/simpleraytracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ class SimpleRaytracer : public RendererServices {
// CPU renderer helpers
void globals_from_hit(ShaderGlobals& sg, const Ray& r,
const Dual2<float>& t, int id);
Vec3 eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx);
Vec3 eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx,
int bounce = -1);
Color3 subpixel_radiance(float x, float y, Sampler& sampler,
ShadingContext* ctx);
Color3 antialias_pixel(int x, int y, ShadingContext* ctx);
Expand Down
5 changes: 5 additions & 0 deletions src/testrender/testrender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ set_shadingsys_options()
shadingsys->attribute("llvm_debugging_symbols", 1);
shadingsys->attribute("llvm_profiling_events", 1);

// We rely on the default set of "raytypes" tags. To use a custom set,
// this is where we would do:
// shadingsys->attribute("raytypes", TypeDesc(TypeDesc::STRING, num_raytypes),
// raytype_names);

shadingsys_options_set = true;
}

Expand Down
17 changes: 10 additions & 7 deletions src/testshade/testshade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,10 @@ static ParamValueList params;
static ParamValueList reparams;
static std::string reparam_layer;
static ErrorHandler errhandler;
static int iters = 1;
static std::string raytype = "camera";
static bool raytype_opt = false;
static int iters = 1;
static std::string raytype_name = "camera";
static int raytype_bit = 0;
static bool raytype_opt = false;
static std::string extraoptions;
static std::string texoptions;
static std::string colorspace;
Expand Down Expand Up @@ -706,7 +707,7 @@ getargs(int argc, const char* argv[])
.help("Specify a full group command");
ap.arg("--archivegroup %s:FILENAME", &archivegroup)
.help("Archive the group to a given filename");
ap.arg("--raytype %s", &raytype)
ap.arg("--raytype %s", &raytype_name)
.help("Set the raytype");
ap.arg("--raytype_opt", &raytype_opt)
.help("Specify ray type mask for optimization");
Expand Down Expand Up @@ -892,7 +893,7 @@ setup_shaderglobals(ShaderGlobals& sg, ShadingSystem* shadingsys, int x, int y)
sg.object2common = OSL::TransformationPtr(&Mobj);

// Just make it look like all shades are the result of 'raytype' rays.
sg.raytype = shadingsys->raytype_bit(ustring(raytype));
sg.raytype = raytype_bit;

// Set up u,v to vary across the "patch", and also their derivatives.
// Note that since u & x, and v & y are aligned, we only need to set
Expand Down Expand Up @@ -1102,10 +1103,10 @@ setup_output_images(SimpleRenderer* rend, ShadingSystem* shadingsys,
// not to actually run the shader.
OSL::PerThreadInfo* thread_info = shadingsys->create_thread_info();
ShadingContext* ctx = shadingsys->get_context(thread_info);
raytype_bit = shadingsys->raytype_bit(ustring(raytype_name));
ShaderGlobals sg;
setup_shaderglobals(sg, shadingsys, 0, 0);

int raytype_bit = shadingsys->raytype_bit(ustring(raytype));
#if OSL_USE_BATCHED
if (batched) {
// jit_group will optimize the group if necesssary
Expand Down Expand Up @@ -1503,6 +1504,8 @@ shade_region(SimpleRenderer* rend, ShaderGroup* shadergroup, OIIO::ROI roi,
// Set up shader globals and a little test grid of points to shade.
ShaderGlobals shaderglobals;

raytype_bit = shadingsys->raytype_bit(ustring(raytype_name));

// Loop over all pixels in the image (in x and y)...
for (int y = roi.ybegin; y < roi.yend; ++y) {
int shadeindex = y * xres + roi.xbegin;
Expand Down Expand Up @@ -1586,7 +1589,7 @@ setup_uniform_shaderglobals(BatchedShaderGlobals<WidthT>& bsg,
usg.renderstate = &bsg;

// Just make it look like all shades are the result of 'raytype' rays.
usg.raytype = shadingsys->raytype_bit(ustring(raytype));
usg.raytype = shadingsys->raytype_bit(ustring(raytype_name));
;


Expand Down
48 changes: 19 additions & 29 deletions testsuite/render-bumptest/glass.osl
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,31 @@

surface
glass
[[ string description = "Simple dielectric material" ]]
[[ string help = "Simple dielectric material" ]]
(
float Ks = 1
[[ string description = "Color scaling",
float UImin = 0, float UIsoftmax = 1 ]],
[[ string help = "Color scaling of the refraction",
float min = 0, float max = 1 ]],
color Cs = 1
[[ string description = "Base color",
float UImin = 0, float UImax = 1 ]],
[[ string help = "Base color",
float min = 0, float max = 1 ]],
float eta = 1.5
[[ string description = "Index of refraction",
float UImin = 1, float UIsoftmax = 2.5 ]],
int caustics = 0
[[ string description = "Enable indirect lighting through glass" ]],
[[ string help = "Index of refraction",
float min = 1, float max = 3 ]],
int TIR = 0
[[ string description = "Enable Total Internal Reflection" ]]
[[ string help = "Enable Total Internal Reflection",
string widget = "checkBox" ]]
)
{
// If the current ray type is glossy or diffuse, that means we
// are computing indirect lighting. Letting that interact with
// glass produces caustics, which are normally hard to trace and
// noisy. So we only return closures if that's not the case or
// the "caustics" flag is on.
if (caustics || (!raytype("glossy") && !raytype("diffuse")))
{
// Take into account backfacing to invert eta accordingly
if (backfacing())
{
Ci = refraction(N, 1.0 / eta);
// If Total Internal Reflection is enabled, we also return a reflection
// closure, which might make rays bounce too much inside an object. That's
// why we make it optional.
if (TIR)
Ci += reflection(N, 1.0 / eta);
}
else
Ci = reflection(N, eta) + refraction(N, eta);
// Take into account backfacing to invert eta accordingly
if (backfacing()) {
Ci = Cs * refraction(N, 1.0 / eta);
// If Total Internal Reflection is enabled, we also return a
// reflection closure, which might make rays bounce too much
// inside an object. That's why we make it optional.
if (TIR)
Ci += Ks * reflection(N, 1.0 / eta);
} else {
Ci = Ks * reflection(N, eta) + Cs * refraction(N, eta);
}
}
1 change: 1 addition & 0 deletions testsuite/render-raytypes/OPTIMIZEONLY
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Render too expensive without optimization
31 changes: 31 additions & 0 deletions testsuite/render-raytypes/glossy.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage


surface
glossy
[[ string description = "Glossy material" ]]
(
color Cs = 0.5
[[ string description = "Base color",
float UImin = 0, float UImax = 1 ]],
color Ce = 1.0
[[ string description = "Edge color",
float UImin = 0, float UImax = 1 ]],
float roughness = 0.5
[[ string description = "Roughness of the surface",
float UImin = 0, float UImax = 1 ]],
float anisotropy = 0.0
[[ string description = "Anisotropy of the surface",
float UImin = 0, float UImax = 1 ]]
)
{
float alpha = roughness * roughness;
float alpha_x = alpha * (1.0 - anisotropy);
float alpha_y = alpha;
vector U = normalize(cross(N, dPdv));
color ior, extinction;
artistic_ior(Cs, Ce, ior, extinction);
Ci = conductor_bsdf(N, U, alpha_x, alpha_y, ior, extinction, "ggx");
}
28 changes: 28 additions & 0 deletions testsuite/render-raytypes/magic.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage


surface
magic
[[ string help = "Magic shader that varies by ray type" ]]
(
float scale_s = 4
[[ string description = "scale factor for s coordinate" ]],
float scale_t = 4
[[ string description = "scale factor for t coordinate" ]],
)
{
float cs = fmod(u * scale_s, 2);
float ct = fmod(v * scale_t, 2);
int check = ((int(cs) ^ int(ct)) == 0);
color Cs = 0;
if (raytype("camera")) {
// Looks like a grey and white checkerboard to camera rays
Cs = check ? 1 : 0.125;
} else {
// Looks like a red and green checkerboard to secondary rays
Cs = check ? color(1,0,0) : color(0,0.25,0);
}
Ci = Cs * diffuse(N);
}
Loading

0 comments on commit a5777fd

Please sign in to comment.