Cycles microdisplacement: Improved automatic bump mapping

Object coordinates can now be used in the displacement shader and will give
correct results, where as before bump mapping was calculated from the displace
positions and resulted in incorrect shading.

This works by evaluating the shader in two parts, first bump then surface, and
setting the shader state to match what it would be if the surface was
undisplaced for the bump shader evaluation. Currently only `P` is set as if
undisplaced, but other shader variables could be set as well, such as `I` or
`time`. Since these aren't set to anything meaningful for displacement I left
them out of this patch, we can decide what to do with them separately.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D2156
This commit is contained in:
Mai Lavelle 2016-08-14 11:44:25 -04:00
parent 62aecbdac1
commit e7ea1ae78c
14 changed files with 181 additions and 52 deletions

@ -113,6 +113,7 @@ set(SRC_SVM_HEADERS
svm/svm.h
svm/svm_attribute.h
svm/svm_blackbody.h
svm/svm_bump.h
svm/svm_camera.h
svm/svm_closure.h
svm/svm_convert.h

@ -714,20 +714,21 @@ enum ShaderDataFlag {
SD_VOLUME_MIS = (1 << 19), /* use multiple importance sampling */
SD_VOLUME_CUBIC = (1 << 20), /* use cubic interpolation for voxels */
SD_HAS_BUMP = (1 << 21), /* has data connected to the displacement input */
SD_HAS_DISPLACEMENT = (1 << 22), /* has true displacement */
SD_SHADER_FLAGS = (SD_USE_MIS|SD_HAS_TRANSPARENT_SHADOW|SD_HAS_VOLUME|
SD_HAS_ONLY_VOLUME|SD_HETEROGENEOUS_VOLUME|
SD_HAS_BSSRDF_BUMP|SD_VOLUME_EQUIANGULAR|SD_VOLUME_MIS|
SD_VOLUME_CUBIC|SD_HAS_BUMP),
SD_VOLUME_CUBIC|SD_HAS_BUMP|SD_HAS_DISPLACEMENT),
/* object flags */
SD_HOLDOUT_MASK = (1 << 22), /* holdout for camera rays */
SD_OBJECT_MOTION = (1 << 23), /* has object motion blur */
SD_TRANSFORM_APPLIED = (1 << 24), /* vertices have transform applied */
SD_NEGATIVE_SCALE_APPLIED = (1 << 25), /* vertices have negative scale applied */
SD_OBJECT_HAS_VOLUME = (1 << 26), /* object has a volume shader */
SD_OBJECT_INTERSECTS_VOLUME = (1 << 27), /* object intersects AABB of an object with volume shader */
SD_OBJECT_HAS_VERTEX_MOTION = (1 << 28), /* has position for motion vertices */
SD_HOLDOUT_MASK = (1 << 23), /* holdout for camera rays */
SD_OBJECT_MOTION = (1 << 24), /* has object motion blur */
SD_TRANSFORM_APPLIED = (1 << 25), /* vertices have transform applied */
SD_NEGATIVE_SCALE_APPLIED = (1 << 26), /* vertices have negative scale applied */
SD_OBJECT_HAS_VOLUME = (1 << 27), /* object has a volume shader */
SD_OBJECT_INTERSECTS_VOLUME = (1 << 28), /* object intersects AABB of an object with volume shader */
SD_OBJECT_HAS_VERTEX_MOTION = (1 << 29), /* has position for motion vertices */
SD_OBJECT_FLAGS = (SD_HOLDOUT_MASK|SD_OBJECT_MOTION|SD_TRANSFORM_APPLIED|
SD_NEGATIVE_SCALE_APPLIED|SD_OBJECT_HAS_VOLUME|

@ -54,6 +54,7 @@ struct OSLGlobals {
vector<OSL::ShaderGroupRef> surface_state;
vector<OSL::ShaderGroupRef> volume_state;
vector<OSL::ShaderGroupRef> displacement_state;
vector<OSL::ShaderGroupRef> bump_state;
OSL::ShaderGroupRef background_state;
/* attributes */

@ -93,6 +93,7 @@ ustring OSLRenderServices::u_geom_numpolyvertices("geom:numpolyvertices");
ustring OSLRenderServices::u_geom_trianglevertices("geom:trianglevertices");
ustring OSLRenderServices::u_geom_polyvertices("geom:polyvertices");
ustring OSLRenderServices::u_geom_name("geom:name");
ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced");
ustring OSLRenderServices::u_is_smooth("geom:is_smooth");
#ifdef __HAIR__
ustring OSLRenderServices::u_is_curve("geom:is_curve");

@ -158,6 +158,7 @@ public:
static ustring u_geom_trianglevertices;
static ustring u_geom_polyvertices;
static ustring u_geom_name;
static ustring u_geom_undisplaced;
static ustring u_is_smooth;
static ustring u_is_curve;
static ustring u_curve_thickness;

@ -184,6 +184,47 @@ void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state
OSL::ShadingContext *octx = tdata->context[(int)ctx];
int shader = sd->shader & SHADER_MASK;
/* automatic bump shader */
if(kg->osl->bump_state[shader]) {
/* save state */
float3 P = sd->P;
float3 dPdx = sd->dP.dx;
float3 dPdy = sd->dP.dy;
/* set state as if undisplaced */
if(sd->flag & SD_HAS_DISPLACEMENT) {
float data[9];
bool found = kg->osl->services->get_attribute(sd, true, OSLRenderServices::u_empty, TypeDesc::TypeVector,
OSLRenderServices::u_geom_undisplaced, data);
assert(found);
memcpy(&sd->P, data, sizeof(float)*3);
memcpy(&sd->dP.dx, data+3, sizeof(float)*3);
memcpy(&sd->dP.dy, data+6, sizeof(float)*3);
object_position_transform(kg, sd, &sd->P);
object_dir_transform(kg, sd, &sd->dP.dx);
object_dir_transform(kg, sd, &sd->dP.dy);
globals->P = TO_VEC3(sd->P);
globals->dPdx = TO_VEC3(sd->dP.dx);
globals->dPdy = TO_VEC3(sd->dP.dy);
}
/* execute bump shader */
ss->execute(octx, *(kg->osl->bump_state[shader]), *globals);
/* reset state */
sd->P = P;
sd->dP.dx = dPdx;
sd->dP.dy = dPdy;
globals->P = TO_VEC3(P);
globals->dPdx = TO_VEC3(dPdx);
globals->dPdy = TO_VEC3(dPdy);
}
/* surface shader */
if(kg->osl->surface_state[shader]) {
ss->execute(octx, *(kg->osl->surface_state[shader]), *globals);
}

@ -181,6 +181,7 @@ CCL_NAMESPACE_END
#include "svm_brick.h"
#include "svm_vector_transform.h"
#include "svm_voxel.h"
#include "svm_bump.h"
CCL_NAMESPACE_BEGIN
@ -294,6 +295,12 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a
case NODE_CLOSURE_SET_NORMAL:
svm_node_set_normal(kg, sd, stack, node.y, node.z);
break;
case NODE_ENTER_BUMP_EVAL:
svm_node_enter_bump_eval(kg, sd, stack, node.y);
break;
case NODE_LEAVE_BUMP_EVAL:
svm_node_leave_bump_eval(kg, sd, stack, node.y);
break;
# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */
case NODE_HSV:
svm_node_hsv(kg, sd, stack, node, &offset);

@ -0,0 +1,54 @@
/*
* Copyright 2011-2016 Blender Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
CCL_NAMESPACE_BEGIN
/* Bump Eval Nodes */
ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, ShaderData *sd, float *stack, uint offset)
{
/* save state */
stack_store_float3(stack, offset+0, ccl_fetch(sd, P));
stack_store_float3(stack, offset+3, ccl_fetch(sd, dP).dx);
stack_store_float3(stack, offset+6, ccl_fetch(sd, dP).dy);
/* set state as if undisplaced */
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
if(desc.offset != ATTR_STD_NOT_FOUND) {
float3 P, dPdx, dPdy;
P = primitive_attribute_float3(kg, sd, desc, &dPdx, &dPdy);
object_position_transform(kg, sd, &P);
object_dir_transform(kg, sd, &dPdx);
object_dir_transform(kg, sd, &dPdy);
ccl_fetch(sd, P) = P;
ccl_fetch(sd, dP).dx = dPdx;
ccl_fetch(sd, dP).dy = dPdy;
}
}
ccl_device void svm_node_leave_bump_eval(KernelGlobals *kg, ShaderData *sd, float *stack, uint offset)
{
/* restore state */
ccl_fetch(sd, P) = stack_load_float3(stack, offset+0);
ccl_fetch(sd, dP).dx = stack_load_float3(stack, offset+3);
ccl_fetch(sd, dP).dy = stack_load_float3(stack, offset+6);
}
CCL_NAMESPACE_END

@ -26,6 +26,8 @@ CCL_NAMESPACE_BEGIN
/* SVM stack offsets with this value indicate that it's not on the stack */
#define SVM_STACK_INVALID 255
#define SVM_BUMP_EVAL_STATE_SIZE 9
/* Nodes */
/* Known frequencies of used nodes, used for selective nodes compilation
@ -127,6 +129,8 @@ typedef enum ShaderNodeType {
NODE_HAIR_INFO,
NODE_UVMAP,
NODE_TEX_VOXEL,
NODE_ENTER_BUMP_EVAL,
NODE_LEAVE_BUMP_EVAL,
} ShaderNodeType;
typedef enum NodeAttributeType {
@ -374,7 +378,8 @@ typedef enum NodeTexVoxelSpace {
typedef enum ShaderType {
SHADER_TYPE_SURFACE,
SHADER_TYPE_VOLUME,
SHADER_TYPE_DISPLACEMENT
SHADER_TYPE_DISPLACEMENT,
SHADER_TYPE_BUMP,
} ShaderType;
/* Closure */

@ -856,27 +856,8 @@ void ShaderGraph::bump_from_displacement()
/* connect the bump out to the set normal in: */
connect(bump->output("Normal"), set_normal->input("Direction"));
/* connect bump output to normal input nodes that aren't set yet. actually
* this will only set the normal input to the geometry node that we created
* and connected to all other normal inputs already. */
foreach(ShaderNode *node, nodes) {
/* Don't connect normal to the bump node we're coming from,
* otherwise it'll be a cycle in graph.
*/
if(node == bump) {
continue;
}
foreach(ShaderInput *input, node->inputs) {
if(!input->link && (input->flags() & SocketType::LINK_NORMAL))
connect(set_normal->output("Normal"), input);
}
}
/* for displacement bump, clear the normal input in case the above loop
* connected the setnormal out to the bump normalin */
ShaderInput *bump_normal_in = bump->input("Normal");
if(bump_normal_in)
bump_normal_in->link = NULL;
/* connect to output node */
connect(set_normal->output("Normal"), output()->input("Normal"));
/* finally, add the copied nodes to the graph. we can't do this earlier
* because we would create dependency cycles in the above loop */

@ -609,7 +609,7 @@ bool OSLCompiler::node_skip_input(ShaderNode *node, ShaderInput *input)
return true;
if(input->name() == "Displacement" && current_type != SHADER_TYPE_DISPLACEMENT)
return true;
if(input->name() == "Normal")
if(input->name() == "Normal" && current_type != SHADER_TYPE_BUMP)
return true;
}
else if(node->special_type == SHADER_SPECIAL_TYPE_BUMP) {
@ -684,6 +684,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath)
ss->Shader("surface", name, id(node).c_str());
else if(current_type == SHADER_TYPE_DISPLACEMENT)
ss->Shader("displacement", name, id(node).c_str());
else if(current_type == SHADER_TYPE_BUMP)
ss->Shader("displacement", name, id(node).c_str());
else
assert(0);
@ -1055,6 +1057,12 @@ OSL::ShaderGroupRef OSLCompiler::compile_type(Shader *shader, ShaderGraph *graph
generate_nodes(dependencies);
output->compile(*this);
}
else if(type == SHADER_TYPE_BUMP) {
/* generate bump shader */
find_dependencies(dependencies, output->input("Normal"));
generate_nodes(dependencies);
output->compile(*this);
}
else if(type == SHADER_TYPE_VOLUME) {
/* generate volume shader */
find_dependencies(dependencies, output->input("Volume"));
@ -1116,10 +1124,10 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader)
if(shader->used && graph && output->input("Surface")->link) {
shader->osl_surface_ref = compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
if(shader->graph_bump)
shader->osl_surface_bump_ref = compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
if(shader->graph_bump && shader->displacement_method != DISPLACE_TRUE)
shader->osl_surface_bump_ref = compile_type(shader, shader->graph_bump, SHADER_TYPE_BUMP);
else
shader->osl_surface_bump_ref = shader->osl_surface_ref;
shader->osl_surface_bump_ref = OSL::ShaderGroupRef();
shader->has_surface = true;
}
@ -1146,15 +1154,10 @@ void OSLCompiler::compile(Scene *scene, OSLGlobals *og, Shader *shader)
}
/* push state to array for lookup */
if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) {
og->surface_state.push_back(shader->osl_surface_ref);
}
else {
og->surface_state.push_back(shader->osl_surface_bump_ref);
}
og->surface_state.push_back(shader->osl_surface_ref);
og->volume_state.push_back(shader->osl_volume_ref);
og->displacement_state.push_back(shader->osl_displacement_ref);
og->bump_state.push_back(shader->osl_surface_bump_ref);
}
#else

@ -407,6 +407,8 @@ void ShaderManager::device_update_common(Device *device,
flag |= SD_VOLUME_CUBIC;
if(shader->graph_bump)
flag |= SD_HAS_BUMP;
if(shader->displacement_method != DISPLACE_BUMP)
flag |= SD_HAS_DISPLACEMENT;
/* shader with bump mapping */
if(shader->displacement_method != DISPLACE_TRUE && shader->graph_bump)

@ -146,9 +146,8 @@ int SVMCompiler::stack_size(SocketType::Type type)
return size;
}
int SVMCompiler::stack_find_offset(SocketType::Type type)
int SVMCompiler::stack_find_offset(int size)
{
int size = stack_size(type);
int offset = -1;
/* find free space in stack & mark as used */
@ -175,6 +174,11 @@ int SVMCompiler::stack_find_offset(SocketType::Type type)
return 0;
}
int SVMCompiler::stack_find_offset(SocketType::Type type)
{
return stack_find_offset(stack_size(type));
}
void SVMCompiler::stack_clear_offset(SocketType::Type type, int offset)
{
int size = stack_size(type);
@ -647,6 +651,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
case SHADER_TYPE_DISPLACEMENT:
clin = node->input("Displacement");
break;
case SHADER_TYPE_BUMP:
clin = node->input("Normal");
break;
default:
assert(0);
break;
@ -663,6 +670,13 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
output->stack_offset = SVM_STACK_INVALID;
}
/* for the bump shader we need add a node to store the shader state */
int bump_state_offset = SVM_STACK_INVALID;
if(type == SHADER_TYPE_BUMP) {
bump_state_offset = stack_find_offset(SVM_BUMP_EVAL_STATE_SIZE);
add_node(NODE_ENTER_BUMP_EVAL, bump_state_offset);
}
if(shader->used) {
if(clin->link) {
bool generate = false;
@ -680,6 +694,9 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
generate = true;
shader->has_displacement = true;
break;
case SHADER_TYPE_BUMP: /* generate bump shader */
generate = true;
break;
default:
break;
}
@ -696,13 +713,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty
node->compile(*this);
}
/* add node to restore state after bump shader has finished */
if(type == SHADER_TYPE_BUMP) {
add_node(NODE_LEAVE_BUMP_EVAL, bump_state_offset);
}
/* if compile failed, generate empty shader */
if(compile_failed) {
svm_nodes.clear();
compile_failed = false;
}
add_node(NODE_END, 0, 0, 0);
/* for bump shaders we fall thru to the surface shader, but if this is any other kind of shader it ends here */
if(type != SHADER_TYPE_BUMP) {
add_node(NODE_END, 0, 0, 0);
}
}
void SVMCompiler::compile(Scene *scene,
@ -752,17 +777,22 @@ void SVMCompiler::compile(Scene *scene,
shader->has_object_dependency = false;
shader->has_integrator_dependency = false;
/* generate surface shader */
if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) {
scoped_timer timer((summary != NULL)? &summary->time_generate_surface: NULL);
compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
/* generate bump shader */
if(shader->displacement_method != DISPLACE_TRUE && shader->graph_bump) {
scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL);
compile_type(shader, shader->graph_bump, SHADER_TYPE_BUMP);
global_svm_nodes[index].y = global_svm_nodes.size();
global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end());
}
else {
scoped_timer timer((summary != NULL)? &summary->time_generate_bump: NULL);
compile_type(shader, shader->graph_bump, SHADER_TYPE_SURFACE);
global_svm_nodes[index].y = global_svm_nodes.size();
/* generate surface shader */
{
scoped_timer timer((summary != NULL)? &summary->time_generate_surface: NULL);
compile_type(shader, shader->graph, SHADER_TYPE_SURFACE);
/* only set jump offset if there's no bump shader, as the bump shader will fall thru to this one if it exists */
if(shader->displacement_method == DISPLACE_TRUE || !shader->graph_bump) {
global_svm_nodes[index].y = global_svm_nodes.size();
}
global_svm_nodes.insert(global_svm_nodes.end(), svm_nodes.begin(), svm_nodes.end());
}

@ -99,6 +99,7 @@ public:
int stack_assign(ShaderInput *input);
int stack_assign_if_linked(ShaderInput *input);
int stack_assign_if_linked(ShaderOutput *output);
int stack_find_offset(int size);
int stack_find_offset(SocketType::Type type);
void stack_clear_offset(SocketType::Type type, int offset);
void stack_link(ShaderInput *input, ShaderOutput *output);