Skip to content

Commit 907ddce

Browse files
authored
testing: testrender: Implement basic displacement shader support (#1898)
Associate an (optional) displacement shader with each material. On startup, we execute the displacement for each face-vertex that has a valid displacement shader assigned. Signed-off-by: Chris Kulla <[email protected]>
1 parent edcd505 commit 907ddce

File tree

12 files changed

+281
-20
lines changed

12 files changed

+281
-20
lines changed

src/cmake/testing.cmake

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,9 @@ macro (osl_add_all_tests)
355355
reparam reparam-arrays reparam-string testoptix-reparam
356356
render-background render-bumptest
357357
render-bunny
358-
render-cornell render-furnace-diffuse
358+
render-cornell
359+
render-displacement
360+
render-furnace-diffuse
359361
render-mx-furnace-burley-diffuse
360362
render-mx-furnace-oren-nayar
361363
render-mx-furnace-sheen

src/testrender/optixraytracer.cpp

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -484,16 +484,17 @@ OptixRaytracer::create_shaders()
484484

485485
for (const auto& groupref : shaders()) {
486486
std::string group_name, fused_name;
487-
shadingsys->getattribute(groupref.get(), "groupname", group_name);
488-
shadingsys->getattribute(groupref.get(), "group_fused_name",
487+
shadingsys->getattribute(groupref.surf.get(), "groupname", group_name);
488+
shadingsys->getattribute(groupref.surf.get(), "group_fused_name",
489489
fused_name);
490490

491-
shadingsys->attribute(groupref.get(), "renderer_outputs",
491+
shadingsys->attribute(groupref.surf.get(), "renderer_outputs",
492492
TypeDesc(TypeDesc::STRING, outputs.size()),
493493
outputs.data());
494-
shadingsys->optimize_group(groupref.get(), nullptr);
494+
shadingsys->optimize_group(groupref.surf.get(), nullptr);
495495

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

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

520521
void* interactive_params = nullptr;
521-
shadingsys->getattribute(groupref.get(), "device_interactive_params",
522-
TypeDesc::PTR, &interactive_params);
522+
shadingsys->getattribute(groupref.surf.get(),
523+
"device_interactive_params", TypeDesc::PTR,
524+
&interactive_params);
523525
material_interactive_params.push_back(interactive_params);
524526

525527
OptixModule optix_module;
@@ -1025,6 +1027,9 @@ OptixRaytracer::prepare_render()
10251027
// Set up the OptiX Context
10261028
init_optix_context(camera.xres, camera.yres);
10271029

1030+
// run displacement
1031+
prepare_geometry();
1032+
10281033
// Set up the OptiX scene graph
10291034
build_accel();
10301035
prepare_lights();

src/testrender/simpleraytracer.cpp

Lines changed: 151 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,27 @@ SimpleRaytracer::parse_scene_xml(const std::string& scenefile)
471471
}
472472
}
473473
shadingsys->ShaderGroupEnd(*group);
474-
if (name_attr)
475-
shadermap.emplace(name_attr.value(), int(shaders().size()));
476-
shaders().emplace_back(group);
474+
if (name_attr) {
475+
if (auto it = shadermap.find(name); it != shadermap.end()) {
476+
int shaderID = it->second;
477+
if (shaderID >= 0 && shaderID < int(shaders().size())) {
478+
fprintf(stderr, "Updating shader %d - %s\n", shaderID,
479+
shadertype.c_str());
480+
// we already have a material under this name,
481+
Material& m = shaders()[shaderID];
482+
// TODO: could we query the shadertype directly from the ShaderGroup?
483+
if (shadertype == "displacement")
484+
m.disp = group;
485+
else if (shadertype == "surface")
486+
m.surf = group;
487+
// skip the rest which would add a new material
488+
continue;
489+
}
490+
} else {
491+
shadermap.emplace(name, int(shaders().size()));
492+
}
493+
}
494+
shaders().emplace_back(Material { group, nullptr });
477495
m_shader_is_light.emplace_back(
478496
is_light_attr ? strtobool(is_light_attr.value()) : false);
479497
} else {
@@ -925,7 +943,7 @@ SimpleRaytracer::eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx,
925943
if (bounce >= 0)
926944
sg.raytype = bounce > 0 ? Ray::DIFFUSE : Ray::CAMERA;
927945
#ifndef __CUDACC__
928-
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID], sg);
946+
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID].surf, sg);
929947
#else
930948
alignas(8) char closure_pool[256];
931949
execute_shader(sg, render_params.bg_id, closure_pool);
@@ -1000,11 +1018,11 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
10001018
int shaderID = scene.shaderid(hit.id);
10011019

10021020
#ifndef __CUDACC__
1003-
if (shaderID < 0 || !m_shaders[shaderID])
1021+
if (shaderID < 0 || !m_shaders[shaderID].surf)
10041022
break; // no shader attached? done
10051023

10061024
// execute shader and process the resulting list of closures
1007-
shadingsys->execute(*ctx, *m_shaders[shaderID], sg);
1025+
shadingsys->execute(*ctx, *m_shaders[shaderID].surf, sg);
10081026
#else
10091027
if (shaderID < 0)
10101028
break; // no shader attached? done
@@ -1116,7 +1134,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
11161134
sample.u, sample.v);
11171135
#ifndef __CUDACC__
11181136
// execute the light shader (for emissive closures only)
1119-
shadingsys->execute(*ctx, *m_shaders[shaderID],
1137+
shadingsys->execute(*ctx, *m_shaders[shaderID].surf,
11201138
light_sg);
11211139
#else
11221140
execute_shader(light_sg, shaderID, light_closure_pool);
@@ -1205,6 +1223,8 @@ SimpleRaytracer::prepare_render()
12051223
backgroundResolution = 0;
12061224
}
12071225

1226+
prepare_geometry();
1227+
12081228
// build bvh and prepare triangles
12091229
scene.prepare(errhandler());
12101230
prepare_lights();
@@ -1241,7 +1261,7 @@ SimpleRaytracer::prepare_lights()
12411261
// collect all light emitting triangles
12421262
for (unsigned t = 0, n = scene.num_prims(); t < n; t++) {
12431263
int shaderID = scene.shaderid(t);
1244-
if (shaderID < 0 || !m_shaders[shaderID])
1264+
if (shaderID < 0 || !m_shaders[shaderID].surf)
12451265
continue; // no shader attached
12461266
if (m_shader_is_light[shaderID])
12471267
m_lightprims.emplace_back(t);
@@ -1252,6 +1272,129 @@ SimpleRaytracer::prepare_lights()
12521272
}
12531273

12541274

1275+
void
1276+
SimpleRaytracer::prepare_geometry()
1277+
{
1278+
bool have_displacement = false;
1279+
for (const Material& m : shaders()) {
1280+
if (m.disp) {
1281+
have_displacement = true;
1282+
break;
1283+
}
1284+
}
1285+
if (have_displacement) {
1286+
errhandler().infofmt("Evaluating displacement shaders");
1287+
// Loop through all triangles and run displacement shader if there is one
1288+
// or copy the input point if there is none
1289+
std::vector<Vec3> disp_verts(scene.verts.size(), Vec3(0, 0, 0));
1290+
std::vector<int> valance(
1291+
scene.verts.size(),
1292+
0); // number of times each vertex has been displaced
1293+
1294+
OSL::PerThreadInfo* thread_info = shadingsys->create_thread_info();
1295+
ShadingContext* ctx = shadingsys->get_context(thread_info);
1296+
1297+
bool has_smooth_normals = false;
1298+
for (int primID = 0, nprims = scene.triangles.size(); primID < nprims;
1299+
primID++) {
1300+
Vec3 p[3], n[3];
1301+
Vec2 uv[3];
1302+
1303+
p[0] = scene.verts[scene.triangles[primID].a];
1304+
p[1] = scene.verts[scene.triangles[primID].b];
1305+
p[2] = scene.verts[scene.triangles[primID].c];
1306+
1307+
valance[scene.triangles[primID].a]++;
1308+
valance[scene.triangles[primID].b]++;
1309+
valance[scene.triangles[primID].c]++;
1310+
1311+
int shaderID = scene.shaderid(primID);
1312+
if (shaderID < 0 || !m_shaders[shaderID].disp) {
1313+
disp_verts[scene.triangles[primID].a] += p[0];
1314+
disp_verts[scene.triangles[primID].b] += p[1];
1315+
disp_verts[scene.triangles[primID].c] += p[2];
1316+
continue;
1317+
}
1318+
1319+
1320+
Vec3 Ng = (p[0] - p[1]).cross(p[0] - p[2]);
1321+
float area = 0.5f * Ng.length();
1322+
Ng = Ng.normalize();
1323+
if (scene.n_triangles[primID].a >= 0) {
1324+
n[0] = scene.normals[scene.n_triangles[primID].a];
1325+
n[1] = scene.normals[scene.n_triangles[primID].b];
1326+
n[2] = scene.normals[scene.n_triangles[primID].c];
1327+
has_smooth_normals = true;
1328+
} else {
1329+
n[0] = n[1] = n[2] = Ng;
1330+
}
1331+
1332+
if (scene.uv_triangles[primID].a >= 0) {
1333+
uv[0] = scene.uvs[scene.uv_triangles[primID].a];
1334+
uv[1] = scene.uvs[scene.uv_triangles[primID].b];
1335+
uv[2] = scene.uvs[scene.uv_triangles[primID].c];
1336+
} else {
1337+
uv[0] = uv[1] = uv[2] = Vec2(0, 0);
1338+
}
1339+
1340+
// displace each vertex
1341+
for (int i = 0; i < 3; i++) {
1342+
ShaderGlobals sg = {};
1343+
sg.P = p[i];
1344+
sg.Ng = Ng;
1345+
sg.N = n[i];
1346+
sg.u = uv[i].x;
1347+
sg.v = uv[i].y;
1348+
sg.I = (p[i] - camera.eye).normalize();
1349+
sg.surfacearea = area;
1350+
sg.renderstate = &sg;
1351+
1352+
shadingsys->execute(*ctx, *m_shaders[shaderID].disp, sg);
1353+
1354+
p[i] = sg.P;
1355+
}
1356+
disp_verts[scene.triangles[primID].a] += p[0];
1357+
disp_verts[scene.triangles[primID].b] += p[1];
1358+
disp_verts[scene.triangles[primID].c] += p[2];
1359+
}
1360+
1361+
// release context
1362+
shadingsys->release_context(ctx);
1363+
shadingsys->destroy_thread_info(thread_info);
1364+
1365+
// average each vertex by the number of times it was displaced
1366+
for (int i = 0, n = scene.verts.size(); i < n; i++) {
1367+
if (valance[i] > 0)
1368+
disp_verts[i] /= float(valance[i]);
1369+
else
1370+
disp_verts[i] = scene.verts[i];
1371+
}
1372+
// replace old data with the new
1373+
scene.verts = std::move(disp_verts);
1374+
1375+
if (has_smooth_normals) {
1376+
// Recompute the vertex normals (if we had some)
1377+
std::vector<Vec3> disp_normals(scene.normals.size(), Vec3(0, 0, 0));
1378+
for (int primID = 0, nprims = scene.triangles.size();
1379+
primID < nprims; primID++) {
1380+
if (scene.n_triangles[primID].a >= 0) {
1381+
Vec3 p[3];
1382+
p[0] = scene.verts[scene.triangles[primID].a];
1383+
p[1] = scene.verts[scene.triangles[primID].b];
1384+
p[2] = scene.verts[scene.triangles[primID].c];
1385+
// don't normalize to weight by area
1386+
Vec3 Ng = (p[0] - p[1]).cross(p[0] - p[2]);
1387+
disp_normals[scene.n_triangles[primID].a] += Ng;
1388+
disp_normals[scene.n_triangles[primID].b] += Ng;
1389+
disp_normals[scene.n_triangles[primID].c] += Ng;
1390+
}
1391+
}
1392+
for (Vec3& n : disp_normals)
1393+
n = n.normalize();
1394+
scene.normals = std::move(disp_normals);
1395+
}
1396+
}
1397+
}
12551398

12561399
void
12571400
SimpleRaytracer::render(int xres, int yres)

src/testrender/simpleraytracer.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919

2020
OSL_NAMESPACE_BEGIN
2121

22+
struct Material {
23+
ShaderGroupRef surf;
24+
ShaderGroupRef disp;
25+
};
26+
27+
using MaterialVec = std::vector<Material>;
2228

2329
class SimpleRaytracer : public RendererServices {
2430
public:
@@ -80,7 +86,8 @@ class SimpleRaytracer : public RendererServices {
8086

8187
virtual void parse_scene_xml(const std::string& scenefile);
8288
virtual void prepare_render();
83-
virtual void prepare_lights();
89+
void prepare_lights();
90+
void prepare_geometry();
8491
virtual void warmup() {}
8592
virtual void render(int xres, int yres);
8693
virtual void clear();
@@ -89,7 +96,7 @@ class SimpleRaytracer : public RendererServices {
8996
virtual void finalize_pixel_buffer() {}
9097

9198
// ShaderGroupRef storage
92-
std::vector<ShaderGroupRef>& shaders() { return m_shaders; }
99+
MaterialVec& shaders() { return m_shaders; }
93100

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

@@ -122,7 +129,7 @@ class SimpleRaytracer : public RendererServices {
122129
int rr_depth = 5;
123130
float show_albedo_scale = 0.0f;
124131
int show_globals = 0;
125-
std::vector<ShaderGroupRef> m_shaders;
132+
MaterialVec m_shaders;
126133
std::vector<bool> m_shader_is_light;
127134
std::vector<float>
128135
m_mesh_surfacearea; // surface area of all triangles in each mesh (one entry per mesh)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
6+
displacement
7+
disp
8+
(
9+
float scale = 1,
10+
float amplitude = 1,
11+
int octaves = 4,
12+
color Cs = 1
13+
)
14+
{
15+
float amount = 0;
16+
point b = P * scale;
17+
float a = amplitude;
18+
for (int i = 0; i < octaves; i++) {
19+
amount += a * noise(b);
20+
b *= 2.0;
21+
a *= 0.5;
22+
}
23+
P += amount * N;
24+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
6+
surface
7+
emitter
8+
[[ string description = "Lambertian emitter material" ]]
9+
(
10+
float power = 1
11+
[[ string description = "Total power of the light",
12+
float UImin = 0 ]],
13+
color Cs = 1
14+
[[ string description = "Base color",
15+
float UImin = 0, float UImax = 1 ]]
16+
)
17+
{
18+
// Because emission() expects a weight in radiance, we must convert by dividing
19+
// the power (in Watts) by the surface area and the factor of PI implied by
20+
// uniform emission over the hemisphere. N.B.: The total power is BEFORE Cs
21+
// filters the color!
22+
Ci = (power / (M_PI * surfacearea())) * Cs * emission();
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright Contributors to the Open Shading Language project.
2+
// SPDX-License-Identifier: BSD-3-Clause
3+
// https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
4+
5+
6+
surface
7+
matte
8+
[[ string description = "Lambertian diffuse material" ]]
9+
(
10+
float Kd = 1
11+
[[ string description = "Diffuse scaling",
12+
float UImin = 0, float UIsoftmax = 1 ]],
13+
color Cs = 1
14+
[[ string description = "Base color",
15+
float UImin = 0, float UImax = 1 ]]
16+
)
17+
{
18+
Ci = Kd * Cs * diffuse (N);
19+
}
257 KB
Binary file not shown.
275 KB
Binary file not shown.
257 KB
Binary file not shown.

0 commit comments

Comments
 (0)