Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

testrender: Implement basic displacement shader support #1898

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/cmake/testing.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,9 @@ macro (osl_add_all_tests)
reparam reparam-arrays reparam-string testoptix-reparam
render-background render-bumptest
render-bunny
render-cornell render-furnace-diffuse
render-cornell
render-displacement
render-furnace-diffuse
render-mx-furnace-burley-diffuse
render-mx-furnace-oren-nayar
render-mx-furnace-sheen
Expand Down
21 changes: 13 additions & 8 deletions src/testrender/optixraytracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,16 +484,17 @@ OptixRaytracer::create_shaders()

for (const auto& groupref : shaders()) {
std::string group_name, fused_name;
shadingsys->getattribute(groupref.get(), "groupname", group_name);
shadingsys->getattribute(groupref.get(), "group_fused_name",
shadingsys->getattribute(groupref.surf.get(), "groupname", group_name);
shadingsys->getattribute(groupref.surf.get(), "group_fused_name",
fused_name);

shadingsys->attribute(groupref.get(), "renderer_outputs",
shadingsys->attribute(groupref.surf.get(), "renderer_outputs",
TypeDesc(TypeDesc::STRING, outputs.size()),
outputs.data());
shadingsys->optimize_group(groupref.get(), nullptr);
shadingsys->optimize_group(groupref.surf.get(), nullptr);

if (!shadingsys->find_symbol(*groupref.get(), ustring(outputs[0]))) {
if (!shadingsys->find_symbol(*groupref.surf.get(),
ustring(outputs[0]))) {
// FIXME: This is for cases where testshade is run with 1x1 resolution
// Those tests may not have a Cout parameter to write to.
if (m_xres > 1 && m_yres > 1) {
Expand All @@ -504,7 +505,7 @@ OptixRaytracer::create_shaders()

// Retrieve the compiled ShaderGroup PTX
std::string osl_ptx;
shadingsys->getattribute(groupref.get(), "ptx_compiled_version",
shadingsys->getattribute(groupref.surf.get(), "ptx_compiled_version",
OSL::TypeDesc::PTR, &osl_ptx);
if (osl_ptx.empty()) {
errhandler().errorfmt("Failed to generate PTX for ShaderGroup {}",
Expand All @@ -518,8 +519,9 @@ OptixRaytracer::create_shaders()
}

void* interactive_params = nullptr;
shadingsys->getattribute(groupref.get(), "device_interactive_params",
TypeDesc::PTR, &interactive_params);
shadingsys->getattribute(groupref.surf.get(),
"device_interactive_params", TypeDesc::PTR,
&interactive_params);
material_interactive_params.push_back(interactive_params);

OptixModule optix_module;
Expand Down Expand Up @@ -1025,6 +1027,9 @@ OptixRaytracer::prepare_render()
// Set up the OptiX Context
init_optix_context(camera.xres, camera.yres);

// run displacement
prepare_geometry();

// Set up the OptiX scene graph
build_accel();
prepare_lights();
Expand Down
159 changes: 151 additions & 8 deletions src/testrender/simpleraytracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,27 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile)
}
}
shadingsys->ShaderGroupEnd(*group);
if (name_attr)
shadermap.emplace(name_attr.value(), int(shaders().size()));
shaders().emplace_back(group);
if (name_attr) {
if (auto it = shadermap.find(name); it != shadermap.end()) {
int shaderID = it->second;
if (shaderID >= 0 && shaderID < int(shaders().size())) {
fprintf(stderr, "Updating shader %d - %s\n", shaderID,
shadertype.c_str());
// we already have a material under this name,
Material& m = shaders()[shaderID];
// TODO: could we query the shadertype directly from the ShaderGroup?
if (shadertype == "displacement")
m.disp = group;
else if (shadertype == "surface")
m.surf = group;
// skip the rest which would add a new material
continue;
}
} else {
shadermap.emplace(name, int(shaders().size()));
}
}
shaders().emplace_back(Material { group, nullptr });
m_shader_is_light.emplace_back(
is_light_attr ? strtobool(is_light_attr.value()) : false);
} else {
Expand Down Expand Up @@ -925,7 +943,7 @@ SimpleRaytracer::eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx,
if (bounce >= 0)
sg.raytype = bounce > 0 ? Ray::DIFFUSE : Ray::CAMERA;
#ifndef __CUDACC__
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID], sg);
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID].surf, sg);
#else
alignas(8) char closure_pool[256];
execute_shader(sg, render_params.bg_id, closure_pool);
Expand Down Expand Up @@ -1000,11 +1018,11 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
int shaderID = scene.shaderid(hit.id);

#ifndef __CUDACC__
if (shaderID < 0 || !m_shaders[shaderID])
if (shaderID < 0 || !m_shaders[shaderID].surf)
break; // no shader attached? done

// execute shader and process the resulting list of closures
shadingsys->execute(*ctx, *m_shaders[shaderID], sg);
shadingsys->execute(*ctx, *m_shaders[shaderID].surf, sg);
#else
if (shaderID < 0)
break; // no shader attached? done
Expand Down Expand Up @@ -1116,7 +1134,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
sample.u, sample.v);
#ifndef __CUDACC__
// execute the light shader (for emissive closures only)
shadingsys->execute(*ctx, *m_shaders[shaderID],
shadingsys->execute(*ctx, *m_shaders[shaderID].surf,
light_sg);
#else
execute_shader(light_sg, shaderID, light_closure_pool);
Expand Down Expand Up @@ -1205,6 +1223,8 @@ SimpleRaytracer::prepare_render()
backgroundResolution = 0;
}

prepare_geometry();

// build bvh and prepare triangles
scene.prepare(errhandler());
prepare_lights();
Expand Down Expand Up @@ -1241,7 +1261,7 @@ SimpleRaytracer::prepare_lights()
// collect all light emitting triangles
for (unsigned t = 0, n = scene.num_prims(); t < n; t++) {
int shaderID = scene.shaderid(t);
if (shaderID < 0 || !m_shaders[shaderID])
if (shaderID < 0 || !m_shaders[shaderID].surf)
continue; // no shader attached
if (m_shader_is_light[shaderID])
m_lightprims.emplace_back(t);
Expand All @@ -1252,6 +1272,129 @@ SimpleRaytracer::prepare_lights()
}


void
SimpleRaytracer::prepare_geometry()
{
bool have_displacement = false;
for (const Material& m : shaders()) {
if (m.disp) {
have_displacement = true;
break;
}
}
if (have_displacement) {
errhandler().infofmt("Evaluating displacement shaders");
// Loop through all triangles and run displacement shader if there is one
// or copy the input point if there is none
std::vector<Vec3> disp_verts(scene.verts.size(), Vec3(0, 0, 0));
std::vector<int> valance(
scene.verts.size(),
0); // number of times each vertex has been displaced

OSL::PerThreadInfo* thread_info = shadingsys->create_thread_info();
ShadingContext* ctx = shadingsys->get_context(thread_info);

bool has_smooth_normals = false;
for (int primID = 0, nprims = scene.triangles.size(); primID < nprims;
primID++) {
Vec3 p[3], n[3];
Vec2 uv[3];

p[0] = scene.verts[scene.triangles[primID].a];
p[1] = scene.verts[scene.triangles[primID].b];
p[2] = scene.verts[scene.triangles[primID].c];

valance[scene.triangles[primID].a]++;
valance[scene.triangles[primID].b]++;
valance[scene.triangles[primID].c]++;

int shaderID = scene.shaderid(primID);
if (shaderID < 0 || !m_shaders[shaderID].disp) {
disp_verts[scene.triangles[primID].a] += p[0];
disp_verts[scene.triangles[primID].b] += p[1];
disp_verts[scene.triangles[primID].c] += p[2];
continue;
}


Vec3 Ng = (p[0] - p[1]).cross(p[0] - p[2]);
float area = 0.5f * Ng.length();
Ng = Ng.normalize();
if (scene.n_triangles[primID].a >= 0) {
n[0] = scene.normals[scene.n_triangles[primID].a];
n[1] = scene.normals[scene.n_triangles[primID].b];
n[2] = scene.normals[scene.n_triangles[primID].c];
has_smooth_normals = true;
} else {
n[0] = n[1] = n[2] = Ng;
}

if (scene.uv_triangles[primID].a >= 0) {
uv[0] = scene.uvs[scene.uv_triangles[primID].a];
uv[1] = scene.uvs[scene.uv_triangles[primID].b];
uv[2] = scene.uvs[scene.uv_triangles[primID].c];
} else {
uv[0] = uv[1] = uv[2] = Vec2(0, 0);
}

// displace each vertex
for (int i = 0; i < 3; i++) {
ShaderGlobals sg = {};
sg.P = p[i];
sg.Ng = Ng;
sg.N = n[i];
sg.u = uv[i].x;
sg.v = uv[i].y;
sg.I = (p[i] - camera.eye).normalize();
sg.surfacearea = area;
sg.renderstate = &sg;

shadingsys->execute(*ctx, *m_shaders[shaderID].disp, sg);

p[i] = sg.P;
}
disp_verts[scene.triangles[primID].a] += p[0];
disp_verts[scene.triangles[primID].b] += p[1];
disp_verts[scene.triangles[primID].c] += p[2];
}

// release context
shadingsys->release_context(ctx);
shadingsys->destroy_thread_info(thread_info);

// average each vertex by the number of times it was displaced
for (int i = 0, n = scene.verts.size(); i < n; i++) {
if (valance[i] > 0)
disp_verts[i] /= float(valance[i]);
else
disp_verts[i] = scene.verts[i];
}
// replace old data with the new
scene.verts = std::move(disp_verts);

if (has_smooth_normals) {
// Recompute the vertex normals (if we had some)
std::vector<Vec3> disp_normals(scene.normals.size(), Vec3(0, 0, 0));
for (int primID = 0, nprims = scene.triangles.size();
primID < nprims; primID++) {
if (scene.n_triangles[primID].a >= 0) {
Vec3 p[3];
p[0] = scene.verts[scene.triangles[primID].a];
p[1] = scene.verts[scene.triangles[primID].b];
p[2] = scene.verts[scene.triangles[primID].c];
// don't normalize to weight by area
Vec3 Ng = (p[0] - p[1]).cross(p[0] - p[2]);
disp_normals[scene.n_triangles[primID].a] += Ng;
disp_normals[scene.n_triangles[primID].b] += Ng;
disp_normals[scene.n_triangles[primID].c] += Ng;
}
}
for (Vec3& n : disp_normals)
n = n.normalize();
scene.normals = std::move(disp_normals);
}
}
}

void
SimpleRaytracer::render(int xres, int yres)
Expand Down
13 changes: 10 additions & 3 deletions src/testrender/simpleraytracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

OSL_NAMESPACE_BEGIN

struct Material {
ShaderGroupRef surf;
ShaderGroupRef disp;
};

using MaterialVec = std::vector<Material>;

class SimpleRaytracer : public RendererServices {
public:
Expand Down Expand Up @@ -80,7 +86,8 @@ class SimpleRaytracer : public RendererServices {

virtual void parse_scene_xml(const std::string& scenefile);
virtual void prepare_render();
virtual void prepare_lights();
void prepare_lights();
void prepare_geometry();
virtual void warmup() {}
virtual void render(int xres, int yres);
virtual void clear();
Expand All @@ -89,7 +96,7 @@ class SimpleRaytracer : public RendererServices {
virtual void finalize_pixel_buffer() {}

// ShaderGroupRef storage
std::vector<ShaderGroupRef>& shaders() { return m_shaders; }
MaterialVec& shaders() { return m_shaders; }

OIIO::ErrorHandler& errhandler() const { return *m_errhandler; }

Expand Down Expand Up @@ -122,7 +129,7 @@ class SimpleRaytracer : public RendererServices {
int rr_depth = 5;
float show_albedo_scale = 0.0f;
int show_globals = 0;
std::vector<ShaderGroupRef> m_shaders;
MaterialVec m_shaders;
std::vector<bool> m_shader_is_light;
std::vector<float>
m_mesh_surfacearea; // surface area of all triangles in each mesh (one entry per mesh)
Expand Down
24 changes: 24 additions & 0 deletions testsuite/render-displacement/disp.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage


displacement
disp
(
float scale = 1,
float amplitude = 1,
int octaves = 4,
color Cs = 1
)
{
float amount = 0;
point b = P * scale;
float a = amplitude;
for (int i = 0; i < octaves; i++) {
amount += a * noise(b);
b *= 2.0;
a *= 0.5;
}
P += amount * N;
}
23 changes: 23 additions & 0 deletions testsuite/render-displacement/emitter.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage


surface
emitter
[[ string description = "Lambertian emitter material" ]]
(
float power = 1
[[ string description = "Total power of the light",
float UImin = 0 ]],
color Cs = 1
[[ string description = "Base color",
float UImin = 0, float UImax = 1 ]]
)
{
// Because emission() expects a weight in radiance, we must convert by dividing
// the power (in Watts) by the surface area and the factor of PI implied by
// uniform emission over the hemisphere. N.B.: The total power is BEFORE Cs
// filters the color!
Ci = (power / (M_PI * surfacearea())) * Cs * emission();
}
19 changes: 19 additions & 0 deletions testsuite/render-displacement/matte.osl
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright Contributors to the Open Shading Language project.
// SPDX-License-Identifier: BSD-3-Clause
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage


surface
matte
[[ string description = "Lambertian diffuse material" ]]
(
float Kd = 1
[[ string description = "Diffuse scaling",
float UImin = 0, float UIsoftmax = 1 ]],
color Cs = 1
[[ string description = "Base color",
float UImin = 0, float UImax = 1 ]]
)
{
Ci = Kd * Cs * diffuse (N);
}
Binary file added testsuite/render-displacement/ref/out-alt1.exr
Binary file not shown.
Binary file added testsuite/render-displacement/ref/out-alticx.exr
Binary file not shown.
Binary file added testsuite/render-displacement/ref/out.exr
Binary file not shown.
10 changes: 10 additions & 0 deletions testsuite/render-displacement/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python

# Copyright Contributors to the Open Shading Language project.
# SPDX-License-Identifier: BSD-3-Clause
# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage

failthresh = 0.01
failpercent = 1
outputs = [ "out.exr" ]
command = testrender("-r 256 256 -aa 8 scene.xml out.exr")
Loading
Loading