Files
test2/intern/cycles/kernel/osl/osl.h
Lukas Stockner 175686f286 Fix: Cycles: Shadow terminator logic does not account for OSL bump mapping
The issue here is that the automatic bump shader in OSL adjusts globals.N,
which is used to define each closure's shading normal, but sd->N remains
as it was (unlike SVM, which sets it from svm_node_set_normal).

This is a problem since some code like the shadow terminator stuff uses sd->N,
so to make behavior consistent the fix is to set it from OSL as well.

Pull Request: https://projects.blender.org/blender/blender/pulls/138092
2025-04-28 14:03:55 +02:00

288 lines
9.4 KiB
C++

/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Adapted code from Open Shading Language. */
#pragma once
/* OSL Shader Engine
*
* Holds all variables to execute and use OSL shaders from the kernel.
*/
#include "kernel/geom/attribute.h"
#include "kernel/geom/primitive.h"
#include "kernel/osl/closures_setup.h"
#include "kernel/osl/types.h"
#include "kernel/util/differential.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline void shaderdata_to_shaderglobals(KernelGlobals kg,
ccl_private ShaderData *sd,
const uint32_t path_flag,
ccl_private ShaderGlobals *globals)
{
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
const differential3 dI = differential_from_compact(sd->wi, sd->dI);
/* copy from shader data to shader globals */
globals->P = sd->P;
globals->dPdx = dP.dx;
globals->dPdy = dP.dy;
globals->I = sd->wi;
globals->dIdx = dI.dx;
globals->dIdy = dI.dy;
globals->N = sd->N;
globals->Ng = sd->Ng;
globals->u = sd->u;
globals->dudx = sd->du.dx;
globals->dudy = sd->du.dy;
globals->v = sd->v;
globals->dvdx = sd->dv.dx;
globals->dvdy = sd->dv.dy;
globals->dPdu = sd->dPdu;
globals->dPdv = sd->dPdv;
globals->time = sd->time;
globals->dtime = 1.0f;
globals->surfacearea = 1.0f;
globals->raytype = path_flag;
globals->flipHandedness = 0;
globals->backfacing = (sd->flag & SD_BACKFACING);
/* shader data to be used in services callbacks */
globals->sd = sd;
globals->shadingStateUniform = nullptr;
globals->thread_index = 0;
globals->shade_index = 0;
/* hacky, we leave it to services to fetch actual object matrix */
globals->shader2common = sd;
globals->object2common = sd;
/* must be set to nullptr before execute */
globals->Ci = nullptr;
}
ccl_device void flatten_closure_tree(KernelGlobals kg,
ccl_private ShaderData *sd,
const uint32_t path_flag,
const ccl_private OSLClosure *closure)
{
int stack_size = 0;
float3 weight = one_float3();
float3 weight_stack[16];
const ccl_private OSLClosure *closure_stack[16];
int layer_stack_level = -1;
float3 layer_albedo = zero_float3();
while (true) {
switch (closure->id) {
case OSL_CLOSURE_MUL_ID: {
const ccl_private OSLClosureMul *mul = static_cast<const ccl_private OSLClosureMul *>(
closure);
weight *= mul->weight;
closure = mul->closure;
continue;
}
case OSL_CLOSURE_ADD_ID: {
if (stack_size >= 16) {
kernel_assert(!"Exhausted OSL closure stack");
break;
}
const ccl_private OSLClosureAdd *add = static_cast<const ccl_private OSLClosureAdd *>(
closure);
closure = add->closureA;
weight_stack[stack_size] = weight;
closure_stack[stack_size++] = add->closureB;
continue;
}
case OSL_CLOSURE_LAYER_ID: {
const ccl_private OSLClosureComponent *comp =
static_cast<const ccl_private OSLClosureComponent *>(closure);
const ccl_private LayerClosure *layer = reinterpret_cast<const ccl_private LayerClosure *>(
comp + 1);
/* Layer closures may not appear in the top layer subtree of another layer closure. */
kernel_assert(layer_stack_level == -1);
if (layer->top != nullptr) {
/* Push base layer onto the stack, will be handled after the top layers */
weight_stack[stack_size] = weight;
closure_stack[stack_size] = layer->base;
/* Start accumulating albedo of the top layers */
layer_stack_level = stack_size++;
layer_albedo = zero_float3();
/* Continue with the top layers */
closure = layer->top;
}
else {
/* No top layer, just continue with base. */
closure = layer->base;
}
continue;
}
#define OSL_CLOSURE_STRUCT_BEGIN(Upper, lower) \
case OSL_CLOSURE_##Upper##_ID: { \
ccl_private const OSLClosureComponent *comp = \
static_cast<ccl_private const OSLClosureComponent *>(closure); \
float3 albedo = one_float3(); \
osl_closure_##lower##_setup(kg, \
sd, \
path_flag, \
weight * comp->weight, \
reinterpret_cast<ccl_private const Upper##Closure *>(comp + 1), \
(layer_stack_level >= 0) ? &albedo : nullptr); \
if (layer_stack_level >= 0) { \
layer_albedo += albedo; \
} \
break; \
}
#include "closures_template.h"
default:
break;
}
/* Pop the next closure from the stack (or return if we're done). */
do {
if (stack_size == 0) {
return;
}
weight = weight_stack[--stack_size];
closure = closure_stack[stack_size];
if (stack_size == layer_stack_level) {
/* We just finished processing the top layers of a Layer closure, so adjust the weight to
* account for the layering. */
weight = closure_layering_weight(layer_albedo, weight);
layer_stack_level = -1;
/* If it's fully occluded, skip the base layer we just popped from the stack and grab
* the next entry instead. */
if (is_zero(weight)) {
continue;
}
}
} while (closure == nullptr);
}
}
#ifndef __KERNEL_GPU__
template<ShaderType type>
void osl_eval_nodes(const ThreadKernelGlobalsCPU *kg,
const void *state,
ShaderData *sd,
uint32_t path_flag);
#else
template<ShaderType type, typename ConstIntegratorGenericState>
ccl_device_inline void osl_eval_nodes(KernelGlobals kg,
ConstIntegratorGenericState state,
ccl_private ShaderData *sd,
const uint32_t path_flag)
{
ShaderGlobals globals;
shaderdata_to_shaderglobals(kg, sd, path_flag, &globals);
const int shader = sd->shader & SHADER_MASK;
# ifdef __KERNEL_OPTIX__
uint8_t closure_pool[1024];
globals.closure_pool = closure_pool;
if (path_flag & PATH_RAY_SHADOW) {
globals.shade_index = -state - 1;
}
else {
globals.shade_index = state + 1;
}
/* For surface shaders, we might have an automatic bump shader that needs to be executed before
* the main shader to update globals.N. */
# if __cplusplus < 201703L
if (type == SHADER_TYPE_SURFACE)
# else
if constexpr (type == SHADER_TYPE_SURFACE)
# endif
{
if (sd->flag & SD_HAS_BUMP) {
/* Save state. */
const float3 P = sd->P;
const float dP = sd->dP;
const packed_float3 dPdx = globals.dPdx;
const packed_float3 dPdy = globals.dPdy;
/* Set state as if undisplaced. */
if (sd->flag & SD_HAS_DISPLACEMENT) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
kernel_assert(desc.offset != ATTR_STD_NOT_FOUND);
differential3 tmp_dP;
sd->P = primitive_surface_attribute<float3>(kg, sd, desc, &tmp_dP.dx, &tmp_dP.dy);
object_position_transform(kg, sd, &sd->P);
object_dir_transform(kg, sd, &tmp_dP.dx);
object_dir_transform(kg, sd, &tmp_dP.dy);
sd->dP = differential_make_compact(tmp_dP);
globals.P = sd->P;
globals.dPdx = tmp_dP.dx;
globals.dPdy = tmp_dP.dy;
}
/* Execute bump shader. */
unsigned int optix_dc_index = 2 /* NUM_CALLABLE_PROGRAM_GROUPS */ + 1 /* camera program */ +
(shader + SHADER_TYPE_BUMP * kernel_data.max_shaders);
optixDirectCall<void>(optix_dc_index,
/* shaderglobals_ptr = */ &globals,
/* groupdata_ptr = */ (void *)nullptr,
/* userdata_base_ptr = */ (void *)nullptr,
/* output_base_ptr = */ (void *)nullptr,
/* shadeindex = */ 0,
/* interactive_params_ptr */ (void *)nullptr);
/* Reset state. */
sd->P = P;
sd->dP = dP;
/* Apply bump output to sd->N since it's used for e.g. shadow terminator logic. */
sd->N = globals.N;
globals.P = P;
globals.dPdx = dPdx;
globals.dPdy = dPdy;
}
}
unsigned int optix_dc_index = 2 /* NUM_CALLABLE_PROGRAM_GROUPS */ + 1 /* camera program */ +
(shader + type * kernel_data.max_shaders);
optixDirectCall<void>(optix_dc_index,
/* shaderglobals_ptr = */ &globals,
/* groupdata_ptr = */ (void *)nullptr,
/* userdata_base_ptr = */ (void *)nullptr,
/* output_base_ptr = */ (void *)nullptr,
/* shadeindex = */ 0,
/* interactive_params_ptr */ (void *)nullptr);
# endif
# if __cplusplus < 201703L
if (type == SHADER_TYPE_DISPLACEMENT) {
# else
if constexpr (type == SHADER_TYPE_DISPLACEMENT) {
# endif
sd->P = globals.P;
}
else if (globals.Ci) {
flatten_closure_tree(kg, sd, path_flag, globals.Ci);
}
}
#endif
CCL_NAMESPACE_END