Skip to content

Commit fc3c72b

Browse files
committed
testrender: Implement basic displacement shader support
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 bda7495 commit fc3c72b

File tree

9 files changed

+256
-11
lines changed

9 files changed

+256
-11
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/simpleraytracer.cpp

Lines changed: 141 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,26 @@ 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, shadertype.c_str());
479+
// we already have a material under this name,
480+
Material& m = shaders()[shaderID];
481+
// TODO: could we query the shadertype directly from the ShaderGroup?
482+
if (shadertype == "displacement")
483+
m.disp = group;
484+
else if (shadertype == "surface")
485+
m.surf = group;
486+
// skip the rest which would add a new material
487+
continue;
488+
}
489+
} else {
490+
shadermap.emplace(name, int(shaders().size()));
491+
}
492+
}
493+
shaders().emplace_back(Material { group, nullptr });
477494
m_shader_is_light.emplace_back(
478495
is_light_attr ? strtobool(is_light_attr.value()) : false);
479496
} else {
@@ -925,7 +942,7 @@ SimpleRaytracer::eval_background(const Dual2<Vec3>& dir, ShadingContext* ctx,
925942
if (bounce >= 0)
926943
sg.raytype = bounce > 0 ? Ray::DIFFUSE : Ray::CAMERA;
927944
#ifndef __CUDACC__
928-
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID], sg);
945+
shadingsys->execute(*ctx, *m_shaders[backgroundShaderID].surf, sg);
929946
#else
930947
alignas(8) char closure_pool[256];
931948
execute_shader(sg, render_params.bg_id, closure_pool);
@@ -1000,11 +1017,11 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
10001017
int shaderID = scene.shaderid(hit.id);
10011018

10021019
#ifndef __CUDACC__
1003-
if (shaderID < 0 || !m_shaders[shaderID])
1020+
if (shaderID < 0 || !m_shaders[shaderID].surf)
10041021
break; // no shader attached? done
10051022

10061023
// execute shader and process the resulting list of closures
1007-
shadingsys->execute(*ctx, *m_shaders[shaderID], sg);
1024+
shadingsys->execute(*ctx, *m_shaders[shaderID].surf, sg);
10081025
#else
10091026
if (shaderID < 0)
10101027
break; // no shader attached? done
@@ -1116,7 +1133,7 @@ SimpleRaytracer::subpixel_radiance(float x, float y, Sampler& sampler,
11161133
sample.u, sample.v);
11171134
#ifndef __CUDACC__
11181135
// execute the light shader (for emissive closures only)
1119-
shadingsys->execute(*ctx, *m_shaders[shaderID],
1136+
shadingsys->execute(*ctx, *m_shaders[shaderID].surf,
11201137
light_sg);
11211138
#else
11221139
execute_shader(light_sg, shaderID, light_closure_pool);
@@ -1205,6 +1222,122 @@ SimpleRaytracer::prepare_render()
12051222
backgroundResolution = 0;
12061223
}
12071224

1225+
bool have_displacement = false;
1226+
for (const Material& m : shaders()) {
1227+
if (m.disp) {
1228+
have_displacement = true;
1229+
break;
1230+
}
1231+
}
1232+
if (have_displacement) {
1233+
errhandler().infofmt("Evaluating displacement shaders");
1234+
// Loop through all triangles and run displacement shader if there is one
1235+
// or copy the input point if there is none
1236+
std::vector<Vec3> disp_verts(scene.verts.size(), Vec3(0, 0, 0));
1237+
std::vector<int> valance(scene.verts.size(), 0); // number of times each vertex has been displaced
1238+
1239+
OSL::PerThreadInfo* thread_info = shadingsys->create_thread_info();
1240+
ShadingContext* ctx = shadingsys->get_context(thread_info);
1241+
1242+
bool has_smooth_normals = false;
1243+
for (int primID = 0, nprims = scene.triangles.size(); primID < nprims; primID++) {
1244+
Vec3 p[3], n[3];
1245+
Vec2 uv[3];
1246+
1247+
p[0] = scene.verts[scene.triangles[primID].a];
1248+
p[1] = scene.verts[scene.triangles[primID].b];
1249+
p[2] = scene.verts[scene.triangles[primID].c];
1250+
1251+
valance[scene.triangles[primID].a]++;
1252+
valance[scene.triangles[primID].b]++;
1253+
valance[scene.triangles[primID].c]++;
1254+
1255+
int shaderID = scene.shaderid(primID);
1256+
if (shaderID < 0 || !m_shaders[shaderID].disp) {
1257+
disp_verts[scene.triangles[primID].a] += p[0];
1258+
disp_verts[scene.triangles[primID].b] += p[1];
1259+
disp_verts[scene.triangles[primID].c] += p[2];
1260+
continue;
1261+
}
1262+
1263+
1264+
Vec3 Ng = (p[0] - p[1]).cross(p[0] - p[2]);
1265+
float area = 0.5f * Ng.length();
1266+
Ng = Ng.normalize();
1267+
if (scene.n_triangles[primID].a >= 0) {
1268+
n[0] = scene.normals[scene.n_triangles[primID].a];
1269+
n[1] = scene.normals[scene.n_triangles[primID].b];
1270+
n[2] = scene.normals[scene.n_triangles[primID].c];
1271+
has_smooth_normals = true;
1272+
} else {
1273+
n[0] = n[1] = n[2] = Ng;
1274+
}
1275+
1276+
if (scene.uv_triangles[primID].a >= 0) {
1277+
uv[0] = scene.uvs[scene.uv_triangles[primID].a];
1278+
uv[1] = scene.uvs[scene.uv_triangles[primID].b];
1279+
uv[2] = scene.uvs[scene.uv_triangles[primID].c];
1280+
} else {
1281+
uv[0] = uv[1] = uv[2] = Vec2(0, 0);
1282+
}
1283+
1284+
// displace each vertex
1285+
for (int i = 0; i < 3; i++) {
1286+
ShaderGlobals sg = {};
1287+
sg.P = p[i];
1288+
sg.Ng = Ng;
1289+
sg.N = n[i];
1290+
sg.u = uv[i].x;
1291+
sg.v = uv[i].y;
1292+
sg.I = (p[i] - camera.eye).normalize();
1293+
sg.surfacearea = area;
1294+
sg.renderstate = &sg;
1295+
1296+
shadingsys->execute(*ctx, *m_shaders[shaderID].disp, sg);
1297+
1298+
p[i] = sg.P;
1299+
}
1300+
disp_verts[scene.triangles[primID].a] += p[0];
1301+
disp_verts[scene.triangles[primID].b] += p[1];
1302+
disp_verts[scene.triangles[primID].c] += p[2];
1303+
}
1304+
1305+
// release context
1306+
shadingsys->release_context(ctx);
1307+
shadingsys->destroy_thread_info(thread_info);
1308+
1309+
// average each vertex by the number of times it was displaced
1310+
for (int i = 0, n = scene.verts.size(); i < n; i++) {
1311+
if (valance[i] > 0)
1312+
disp_verts[i] /= float(valance[i]);
1313+
else
1314+
disp_verts[i] = scene.verts[i];
1315+
}
1316+
// replace old data with the new
1317+
scene.verts = std::move(disp_verts);
1318+
1319+
if (has_smooth_normals) {
1320+
// Recompute the vertex normals (if we had some)
1321+
std::vector<Vec3> disp_normals(scene.normals.size(), Vec3(0, 0, 0));
1322+
for (int primID = 0, nprims = scene.triangles.size(); primID < nprims; primID++) {
1323+
if (scene.n_triangles[primID].a >= 0) {
1324+
Vec3 p[3];
1325+
p[0] = scene.verts[scene.triangles[primID].a];
1326+
p[1] = scene.verts[scene.triangles[primID].b];
1327+
p[2] = scene.verts[scene.triangles[primID].c];
1328+
// don't normalize to weight by area
1329+
Vec3 Ng = (p[0] - p[1]).cross(p[0] - p[2]);
1330+
disp_normals[scene.n_triangles[primID].a] += Ng;
1331+
disp_normals[scene.n_triangles[primID].b] += Ng;
1332+
disp_normals[scene.n_triangles[primID].c] += Ng;
1333+
}
1334+
}
1335+
for (Vec3& n : disp_normals)
1336+
n = n.normalize();
1337+
scene.normals = std::move(disp_normals);
1338+
}
1339+
}
1340+
12081341
// build bvh and prepare triangles
12091342
scene.prepare(errhandler());
12101343
prepare_lights();
@@ -1241,7 +1374,7 @@ SimpleRaytracer::prepare_lights()
12411374
// collect all light emitting triangles
12421375
for (unsigned t = 0, n = scene.num_prims(); t < n; t++) {
12431376
int shaderID = scene.shaderid(t);
1244-
if (shaderID < 0 || !m_shaders[shaderID])
1377+
if (shaderID < 0 || !m_shaders[shaderID].surf)
12451378
continue; // no shader attached
12461379
if (m_shader_is_light[shaderID])
12471380
m_lightprims.emplace_back(t);

src/testrender/simpleraytracer.h

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

2020
OSL_NAMESPACE_ENTER
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:
@@ -89,7 +95,7 @@ class SimpleRaytracer : public RendererServices {
8995
virtual void finalize_pixel_buffer() {}
9096

9197
// ShaderGroupRef storage
92-
std::vector<ShaderGroupRef>& shaders() { return m_shaders; }
98+
MaterialVec& shaders() { return m_shaders; }
9399

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

@@ -122,7 +128,7 @@ class SimpleRaytracer : public RendererServices {
122128
int rr_depth = 5;
123129
float show_albedo_scale = 0.0f;
124130
int show_globals = 0;
125-
std::vector<ShaderGroupRef> m_shaders;
131+
MaterialVec m_shaders;
126132
std::vector<bool> m_shader_is_light;
127133
std::vector<float>
128134
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.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright Contributors to the Open Shading Language project.
4+
# SPDX-License-Identifier: BSD-3-Clause
5+
# https://github.com/AcademySoftwareFoundation/OpenShadingLanguage
6+
7+
failthresh = 0.01
8+
failpercent = 1
9+
outputs = [ "out.exr" ]
10+
command = testrender("-r 256 256 -aa 8 scene.xml out.exr")
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<World>
2+
<Camera eye="0, 1.5, 25" dir="0,0,-1" fov="14.5" />
3+
4+
<ShaderGroup name="main">color Cs 0.35 0.35 0.35; shader matte layer1;</ShaderGroup>
5+
<ShaderGroup name="main" type="displacement">
6+
float scale 4;
7+
float amplitude 0.3;
8+
int octaves 8;
9+
shader disp layer1;
10+
</ShaderGroup>
11+
12+
<Sphere center="0,1.25,-0.25" radius="0.6" resolution="256" />
13+
14+
<ShaderGroup>color Cs 0.75 0.25 0.25; shader matte layer1;</ShaderGroup>
15+
<Quad corner="-1.5, 0, -1.5" edge_x="0,3,0" edge_y="0,0,3" /> <!-- Left -->
16+
17+
<ShaderGroup>color Cs 0.25 0.25 0.75; shader matte layer1;</ShaderGroup>
18+
<Quad corner="1.5, 0, -1.5" edge_x="0,0,3" edge_y="0,3,0" /> <!-- Right -->
19+
20+
<ShaderGroup>color Cs 0.25 0.25 0.25; shader matte layer1;</ShaderGroup>
21+
<Quad corner="-1.5, 0,-1.5" edge_x="3,0,0" edge_y="0,3,0" /> <!-- Back -->
22+
<Quad corner="-1.5, 0,-1.5" edge_x="0,0,3" edge_y="3,0,0" /> <!-- Botm -->
23+
24+
<Quad corner="-1.5,3,-1.5" edge_x="3,0,0" edge_y="0,0,3" /> <!-- Top -->
25+
26+
<ShaderGroup is_light="yes">float power 100; shader emitter layer1</ShaderGroup>
27+
<Quad corner="-0.5,2.98,-0.5" edge_x="1, 0, 0" edge_y="0, 0, 1"/> <!--Lite -->
28+
</World>

0 commit comments

Comments
 (0)