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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
149 changes: 141 additions & 8 deletions src/testrender/simpleraytracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,26 @@ 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 +942,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 +1017,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 +1133,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 +1222,122 @@ SimpleRaytracer::prepare_render()
backgroundResolution = 0;
}

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);
}
}

// build bvh and prepare triangles
scene.prepare(errhandler());
prepare_lights();
Expand Down Expand Up @@ -1241,7 +1374,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 Down
10 changes: 8 additions & 2 deletions src/testrender/simpleraytracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@

OSL_NAMESPACE_ENTER

struct Material {
ShaderGroupRef surf;
ShaderGroupRef disp;
};

using MaterialVec = std::vector<Material>;

class SimpleRaytracer : public RendererServices {
public:
Expand Down Expand Up @@ -89,7 +95,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 +128,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.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")
28 changes: 28 additions & 0 deletions testsuite/render-displacement/scene.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<World>
<Camera eye="0, 1.5, 25" dir="0,0,-1" fov="14.5" />

<ShaderGroup name="main">color Cs 0.35 0.35 0.35; shader matte layer1;</ShaderGroup>
<ShaderGroup name="main" type="displacement">
float scale 4;
float amplitude 0.3;
int octaves 8;
shader disp layer1;
</ShaderGroup>

<Sphere center="0,1.25,-0.25" radius="0.6" resolution="256" />

<ShaderGroup>color Cs 0.75 0.25 0.25; shader matte layer1;</ShaderGroup>
<Quad corner="-1.5, 0, -1.5" edge_x="0,3,0" edge_y="0,0,3" /> <!-- Left -->

<ShaderGroup>color Cs 0.25 0.25 0.75; shader matte layer1;</ShaderGroup>
<Quad corner="1.5, 0, -1.5" edge_x="0,0,3" edge_y="0,3,0" /> <!-- Right -->

<ShaderGroup>color Cs 0.25 0.25 0.25; shader matte layer1;</ShaderGroup>
<Quad corner="-1.5, 0,-1.5" edge_x="3,0,0" edge_y="0,3,0" /> <!-- Back -->
<Quad corner="-1.5, 0,-1.5" edge_x="0,0,3" edge_y="3,0,0" /> <!-- Botm -->

<Quad corner="-1.5,3,-1.5" edge_x="3,0,0" edge_y="0,0,3" /> <!-- Top -->

<ShaderGroup is_light="yes">float power 100; shader emitter layer1</ShaderGroup>
<Quad corner="-0.5,2.98,-0.5" edge_x="1, 0, 0" edge_y="0, 0, 1"/> <!--Lite -->
</World>
Loading