/* SPDX-FileCopyrightText: 2011-2026 Blender Foundation * SPDX-FileCopyrightText: Contributors to the OpenImageIO project * * SPDX-License-Identifier: Apache-2.0 */ /* Shared functions between OSL on CPU and GPU. */ #pragma once #include "kernel/camera/camera.h" #include "kernel/geom/attribute.h" #include "kernel/geom/curve.h" #include "kernel/geom/gsplat.h" #include "kernel/geom/motion_triangle.h" #include "kernel/geom/object.h" #include "kernel/geom/point.h" #include "kernel/geom/primitive.h" #include "kernel/geom/triangle.h" #include "kernel/image.h" #include "kernel/osl/strings.h" #include "kernel/util/differential.h" #include "kernel/util/ies.h" #include "kernel/util/image_2d.h" #include "kernel/util/image_3d.h" #include "util/hash.h" #ifndef __KERNEL_GPU__ # include "kernel/svm/ao.h" # include "kernel/svm/bevel.h" #endif CCL_NAMESPACE_BEGIN /* For GPU duplicate part of OpenImageIO TypeDesc as we don't use the headers at compile time. */ #ifdef __KERNEL_GPU__ struct TypeDesc { enum BASETYPE { UNKNOWN = 0, NONE = 1, UCHAR = 2, CHAR = 3, USHORT = 4, SHORT = 5, UINT = 6, INT = 7, ULONGLONG = 8, LONGLONG = 9, HALF = 10, FLOAT = 11, DOUBLE = 12, STRING = 13, PTR = 14, LAST = 15, }; enum AGGREGATE { SCALAR = 1, VEC2 = 2, VEC3 = 3, VEC4 = 4, MATRIX33 = 15, MATRIX44 = 16, }; enum VECSEMANTICS { NOSEMANTICS = 0, COLOR = 1, POINT = 2, VECTOR = 3, NORMAL = 4, TIMECODE = 5, }; unsigned char basetype; unsigned char aggregate; unsigned char vecsemantics; unsigned char reserved; int arraylen; ccl_device_inline_method constexpr TypeDesc(BASETYPE b = UNKNOWN, AGGREGATE a = SCALAR, VECSEMANTICS v = NOSEMANTICS, int l = 0) : basetype(b), aggregate(a), vecsemantics(v), reserved(0), arraylen(l) { } ccl_device_inline_method bool operator==(const ccl_private TypeDesc &other) const { return basetype == other.basetype && aggregate == other.aggregate && vecsemantics == other.vecsemantics && arraylen == other.arraylen; } ccl_device_inline_method bool operator!=(const ccl_private TypeDesc &other) const { return !(*this == other); } }; ccl_device_constant TypeDesc TypeFloat(TypeDesc::FLOAT, TypeDesc::SCALAR); ccl_device_constant TypeDesc TypeInt(TypeDesc::INT, TypeDesc::SCALAR); ccl_device_constant TypeDesc TypeString(TypeDesc::STRING, TypeDesc::SCALAR); ccl_device_constant TypeDesc TypeMatrix(TypeDesc::FLOAT, TypeDesc::MATRIX44); #endif /* Type Checking, recognizing both arrays and vectors. */ ccl_device_inline bool is_type_float2(const TypeDesc t) { return (t.basetype == TypeDesc::FLOAT && t.aggregate == TypeDesc::VEC2 && t.arraylen == 0) || t == TypeDesc(TypeDesc::FLOAT, TypeDesc::SCALAR, TypeDesc::NOSEMANTICS, 2); } ccl_device_inline bool is_type_float3(const TypeDesc t) { return (t.basetype == TypeDesc::FLOAT && t.aggregate == TypeDesc::VEC3 && t.arraylen == 0) || t == TypeDesc(TypeDesc::FLOAT, TypeDesc::SCALAR, TypeDesc::NOSEMANTICS, 3); } ccl_device_inline bool is_type_float4(const TypeDesc t) { return (t.basetype == TypeDesc::FLOAT && t.aggregate == TypeDesc::VEC4 && t.arraylen == 0) || t == TypeDesc(TypeDesc::FLOAT, TypeDesc::SCALAR, TypeDesc::NOSEMANTICS, 4); } ccl_device_inline bool is_type_int2(const TypeDesc t) { return (t.basetype == TypeDesc::INT && t.aggregate == TypeDesc::VEC2 && t.arraylen == 0) || t == TypeDesc(TypeDesc::INT, TypeDesc::SCALAR, TypeDesc::NOSEMANTICS, 2); } ccl_device_inline bool is_type_int3(const TypeDesc t) { return (t.basetype == TypeDesc::INT && t.aggregate == TypeDesc::VEC3 && t.arraylen == 0) || t == TypeDesc(TypeDesc::INT, TypeDesc::SCALAR, TypeDesc::NOSEMANTICS, 3); } /* Attribute Utilities */ template ccl_device_inline bool set_attribute(const dual v, const TypeDesc type, bool derivatives, ccl_private void *val); ccl_device_inline void set_data_float(const dual1 data, bool derivatives, ccl_private void *val) { ccl_private float *fval = static_cast(val); fval[0] = data.val; if (derivatives) { fval[1] = data.dx; fval[2] = data.dy; } } ccl_device_inline void set_data_float3(const dual3 data, bool derivatives, ccl_private void *val) { ccl_private float *fval = static_cast(val); copy_v3_v3(fval, data.val); if (derivatives) { copy_v3_v3(fval + 3, data.dx); copy_v3_v3(fval + 6, data.dy); } } ccl_device_inline void set_data_float4(const dual4 data, bool derivatives, ccl_private void *val) { ccl_private float *fval = static_cast(val); copy_v4_v4(fval, data.val); if (derivatives) { copy_v4_v4(fval + 4, data.dx); copy_v4_v4(fval + 8, data.dy); } } ccl_device_inline void set_data_quaternion(const dual data, bool derivatives, ccl_private void *val) { ccl_private float *fval = static_cast(val); copy_v4_qt(fval, data.val); if (derivatives) { copy_v4_qt(fval + 4, data.dx); copy_v4_qt(fval + 8, data.dy); } } /* Matrix Utilities */ ccl_device_forceinline void copy_matrix(ccl_private float *res, const Transform &tfm) { res[0] = tfm.x.x; res[1] = tfm.y.x; res[2] = tfm.z.x; res[3] = 0.0f; res[4] = tfm.x.y; res[5] = tfm.y.y; res[6] = tfm.z.y; res[7] = 0.0f; res[8] = tfm.x.z; res[9] = tfm.y.z; res[10] = tfm.z.z; res[11] = 0.0f; res[12] = tfm.x.w; res[13] = tfm.y.w; res[14] = tfm.z.w; res[15] = 1.0f; } ccl_device_forceinline void copy_matrix(ccl_private float *res, const ProjectionTransform &tfm) { res[0] = tfm.x.x; res[1] = tfm.y.x; res[2] = tfm.z.x; res[3] = tfm.w.x; res[4] = tfm.x.y; res[5] = tfm.y.y; res[6] = tfm.z.y; res[7] = tfm.w.y; res[8] = tfm.x.z; res[9] = tfm.y.z; res[10] = tfm.z.z; res[11] = tfm.w.z; res[12] = tfm.x.w; res[13] = tfm.y.w; res[14] = tfm.z.w; res[15] = tfm.w.w; } /* Matrix */ ccl_device_inline bool osl_shared_get_object_matrix(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float *res) { const int object = sd->object; if (object != OBJECT_NONE) { const Transform tfm = object_get_transform(kg, sd); copy_matrix(res, tfm); return true; } return false; } ccl_device_inline bool osl_shared_get_object_matrix_motion(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float *res, float time) { const int object = sd->object; if (object != OBJECT_NONE) { #ifdef __OBJECT_MOTION__ Transform tfm; if (time == sd->time) { tfm = object_get_transform(kg, sd); } else { tfm = object_fetch_transform_motion_test(kg, object, time, nullptr); } #else const Transform tfm = object_get_transform(kg, sd); #endif copy_matrix(res, tfm); return true; } return false; } ccl_device_inline bool osl_shared_get_object_inverse_matrix(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float *res) { const int object = sd->object; if (object != OBJECT_NONE) { const Transform tfm = object_get_inverse_transform(kg, sd); copy_matrix(res, tfm); return true; } return false; } ccl_device_inline bool osl_shared_get_object_inverse_matrix_motion( KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private float *res, float time) { const int object = sd->object; if (object != OBJECT_NONE) { #ifdef __OBJECT_MOTION__ Transform itfm; if (time == sd->time) { itfm = object_get_inverse_transform(kg, sd); } else { object_fetch_transform_motion_test(kg, object, time, &itfm); } #else const Transform itfm = object_get_inverse_transform(kg, sd); #endif copy_matrix(res, itfm); return true; } return false; } ccl_device_inline bool osl_shared_get_named_matrix(KernelGlobals kg, DeviceString from, ccl_private float *res) { if (from == DeviceStrings::u_ndc) { copy_matrix(res, kernel_data.cam.ndctoworld); return true; } if (from == DeviceStrings::u_raster) { copy_matrix(res, kernel_data.cam.rastertoworld); return true; } if (from == DeviceStrings::u_screen) { copy_matrix(res, kernel_data.cam.screentoworld); return true; } if (from == DeviceStrings::u_camera) { copy_matrix(res, kernel_data.cam.cameratoworld); return true; } if (from == DeviceStrings::u_world) { copy_matrix(res, projection_identity()); return true; } return false; } ccl_device_inline bool osl_shared_get_named_inverse_matrix(KernelGlobals kg, DeviceString to, ccl_private float *res) { if (to == DeviceStrings::u_ndc) { copy_matrix(res, kernel_data.cam.worldtondc); return true; } if (to == DeviceStrings::u_raster) { copy_matrix(res, kernel_data.cam.worldtoraster); return true; } if (to == DeviceStrings::u_screen) { copy_matrix(res, kernel_data.cam.worldtoscreen); return true; } if (to == DeviceStrings::u_camera) { copy_matrix(res, kernel_data.cam.worldtocamera); return true; } if (to == DeviceStrings::u_world) { copy_matrix(res, projection_identity()); return true; } return false; } /* Attribute Setting */ ccl_device_template_spec bool set_attribute(const dual1 v, const TypeDesc type, bool derivatives, ccl_private void *val) { if (is_type_float4(type)) { set_data_float4(make_float4(make_float3(v)), derivatives, val); return true; } if (is_type_float3(type)) { set_data_float3(make_float3(v), derivatives, val); return true; } if (type == TypeFloat) { set_data_float(v, derivatives, val); return true; } return false; } ccl_device_template_spec bool set_attribute(const dual2 v, const TypeDesc type, bool derivatives, ccl_private void *val) { if (is_type_float4(type)) { set_data_float4(make_float4(make_float3(v)), derivatives, val); return true; } if (is_type_float3(type)) { set_data_float3(make_float3(v), derivatives, val); return true; } if (type == TypeFloat) { set_data_float(average(v), derivatives, val); return true; } return false; } ccl_device_template_spec bool set_attribute(const dual3 v, const TypeDesc type, bool derivatives, ccl_private void *val) { if (is_type_float4(type)) { set_data_float4(make_float4(v), derivatives, val); return true; } if (is_type_float3(type)) { set_data_float3(v, derivatives, val); return true; } if (type == TypeFloat) { set_data_float(average(v), derivatives, val); return true; } return false; } ccl_device_template_spec bool set_attribute(const dual4 v, const TypeDesc type, bool derivatives, ccl_private void *val) { if (is_type_float4(type)) { set_data_float4(v, derivatives, val); return true; } if (is_type_float3(type)) { set_data_float3(make_float3(v), derivatives, val); return true; } if (type == TypeFloat) { set_data_float(average(make_float3(v)), derivatives, val); return true; } return false; } ccl_device_template_spec bool set_attribute(const dual v, const TypeDesc type, bool derivatives, ccl_private void *val) { if (is_type_float4(type)) { set_data_quaternion(v, derivatives, val); return true; } if (is_type_float3(type)) { set_data_float3(make_float3(v), derivatives, val); return true; } if (type == TypeFloat) { set_data_float(average(make_float3(v)), derivatives, val); return true; } return false; } template ccl_device_inline bool set_attribute(const T f, const TypeDesc type, bool derivatives, ccl_private void *val) { return set_attribute(dual(f), type, derivatives, val); } ccl_device_template_spec bool set_attribute(const int i, const TypeDesc type, bool derivatives, ccl_private void *val) { if (type == TypeInt) { ccl_private int *ival = static_cast(val); ival[0] = i; if (derivatives) { ival[1] = 0; ival[2] = 0; } return true; } return false; } #ifndef __KERNEL_GPU__ ccl_device_template_spec bool set_attribute(ustring str, const TypeDesc type, bool derivatives, ccl_private void *val) { if (type == TypeString) { OSLUStringHash *sval = static_cast(val); sval[0] = str; if (derivatives) { sval[1] = OSLUStringHash(); sval[2] = OSLUStringHash(); } return true; } return false; } #endif ccl_device_inline bool set_attribute_matrix(const Transform &tfm, const TypeDesc type, ccl_private void *val) { if (type == TypeMatrix) { copy_matrix(static_cast(val), tfm); return true; } return false; } ccl_device_inline bool set_attribute_float3_3(const float3 P[3], TypeDesc type, bool derivatives, ccl_private void *val) { if (type.vecsemantics == TypeDesc::POINT && type.arraylen >= 3) { ccl_private float *fval = static_cast(val); copy_v3_v3(fval, P[0]); copy_v3_v3(fval + 3, P[1]); copy_v3_v3(fval + 6, P[2]); if (type.arraylen > 3) { for (int i = 3 * 3; i < type.arraylen * 3; i++) { fval[i] = 0.0f; } } if (derivatives) { for (int i = type.arraylen * 3; i < type.arraylen * 3 * 3; i++) { fval[i] = 0.0f; } } return true; } return false; } ccl_device bool attribute_bump_map_normal(KernelGlobals kg, ccl_private const ShaderData *sd, ccl_private dual3 &f) { if (!(sd->type & PRIMITIVE_TRIANGLE) || !(sd->shader & SHADER_SMOOTH_NORMAL)) { /* TODO: implement for curve. */ return false; } const bool backfacing = (sd->runtime_flag & SR_BACKFACING); /* Fallback when the smooth normal is zero. */ float3 Ng = backfacing ? -sd->Ng : sd->Ng; object_inverse_normal_transform(kg, sd, &Ng); if (sd->type == PRIMITIVE_TRIANGLE) { f.val = triangle_smooth_normal( kg, Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v, sd->du, sd->dv, f.dx, f.dy); } else { kernel_assert(sd->type & PRIMITIVE_MOTION_TRIANGLE); f.val = motion_triangle_smooth_normal( kg, Ng, sd->object, sd->prim, sd->time, sd->u, sd->v, sd->du, sd->dv, f.dx, f.dy); } if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) { /* Transform to local space. */ object_inverse_normal_transform(kg, sd, &f.val); object_inverse_normal_transform(kg, sd, &f.dx); object_inverse_normal_transform(kg, sd, &f.dy); } if (backfacing) { f = -f; } f.dx -= f.val; f.dy -= f.val; return true; } /* Textures */ ccl_device_forceinline void rgba_to_nchannels(const float4 rgba, const int nchannels, ccl_private float *result) { if (nchannels > 0) { result[0] = rgba.x; } if (nchannels > 1) { result[1] = rgba.y; } if (nchannels > 2) { result[2] = rgba.z; } if (nchannels > 3) { result[3] = rgba.w; } } ccl_device_forceinline float4 get_missingcolor(const float *missingcolor, const int nchannels) { if (!missingcolor) { return IMAGE_MISSING_RGBA; } float4 result = zero_float4(); if (nchannels > 0) { result.x = missingcolor[0]; } if (nchannels > 1) { result.y = missingcolor[1]; } if (nchannels > 2) { result.z = missingcolor[2]; } if (nchannels > 3) { result.w = missingcolor[3]; } return result; } ccl_device_forceinline void missingcolor_to_nchannels(const float *missingcolor, const int nchannels, ccl_private float *result) { if (nchannels > 0) { result[0] = missingcolor[0]; } if (nchannels > 1) { result[1] = missingcolor[1]; } if (nchannels > 2) { result[2] = missingcolor[2]; } if (nchannels > 3) { result[3] = missingcolor[3]; } } ccl_device bool osl_shared_get_texture_info(KernelGlobals kg, ccl_private void *texture_handle, const float2 uv, const bool use_uv, DeviceString dataname, const TypeDesc datatype, void *data) { const OSLTextureHandleType texture_type = OSL_TEXTURE_HANDLE_TYPE(texture_handle); const int image_texture_or_udim_id = OSL_TEXTURE_HANDLE_ID(texture_handle); if (texture_type != OSLTextureHandleType::IMAGE) { return false; } int image_texture_id = image_texture_or_udim_id; if (use_uv) { float2 local_uv = uv; image_texture_id = kernel_image_udim_map(kg, image_texture_or_udim_id, local_uv); } if (image_texture_id == KERNEL_IMAGE_NONE) { return false; } const ccl_global KernelImageTexture &tex = kernel_data_fetch(image_textures, image_texture_id); if (tex.image_info_id == KERNEL_IMAGE_NONE) { return false; } if (dataname == DeviceStrings::u_resolution) { if (is_type_int2(datatype)) { int *res = static_cast(data); res[0] = int(tex.width); res[1] = int(tex.height); return true; } if (is_type_float2(datatype)) { float *res = static_cast(data); res[0] = float(tex.width); res[1] = float(tex.height); return true; } if (is_type_int3(datatype)) { int *res = static_cast(data); res[0] = int(tex.width); res[1] = int(tex.height); res[2] = 1; return true; } if (is_type_float3(datatype)) { float *res = static_cast(data); res[0] = float(tex.width); res[1] = float(tex.height); res[2] = 1.0f; return true; } } else if (dataname == DeviceStrings::u_channels) { if (datatype == TypeInt) { const int image_info_id = tex.image_info_id; const ccl_global KernelImageInfo &info = kernel_data_fetch(image_info, image_info_id); int channels = 4; switch (info.data_type) { case IMAGE_DATA_TYPE_FLOAT4: case IMAGE_DATA_TYPE_BYTE4: case IMAGE_DATA_TYPE_HALF4: case IMAGE_DATA_TYPE_USHORT4: case IMAGE_DATA_TYPE_NANOVDB_FLOAT4: channels = 4; break; case IMAGE_DATA_TYPE_NANOVDB_FLOAT3: channels = 3; break; case IMAGE_DATA_TYPE_FLOAT: case IMAGE_DATA_TYPE_BYTE: case IMAGE_DATA_TYPE_HALF: case IMAGE_DATA_TYPE_USHORT: case IMAGE_DATA_TYPE_NANOVDB_FLOAT: case IMAGE_DATA_TYPE_NANOVDB_FPN: case IMAGE_DATA_TYPE_NANOVDB_FP16: channels = 1; break; case IMAGE_DATA_TYPE_NANOVDB_EMPTY: channels = 0; break; } *static_cast(data) = channels; return true; } } else if (dataname == DeviceStrings::u_exists) { if (datatype == TypeInt) { *static_cast(data) = 1; return true; } } else if (dataname == DeviceStrings::u_averagecolor) { if (is_type_float3(datatype) || is_type_float4(datatype)) { float *res = (float *)data; res[0] = tex.average_color.x; res[1] = tex.average_color.y; res[2] = tex.average_color.z; if (is_type_float4(datatype)) { res[3] = tex.average_color.w; } return true; } } return false; } ccl_device bool osl_shared_texture(KernelGlobals kg, ccl_private ShaderGlobals *sg, ccl_private void *texture_handle, ccl_private OSLTextureOptions *opt, float s, float t, float dsdx, float dtdx, float dsdy, float dtdy, int nchannels, float *result) { const OSLTextureHandleType type = OSL_TEXTURE_HANDLE_TYPE(texture_handle); const int image_texture_or_udim_id = OSL_TEXTURE_HANDLE_ID(texture_handle); ccl_private ShaderData *sd = sg->sd; bool status = false; switch (type) { case OSLTextureHandleType::IMAGE: { const dual2 uv({s, t}, {dsdx, dtdx}, {dsdy, dtdy}); const float4 rgba = kernel_image_interp_with_udim( kg, sd, image_texture_or_udim_id, uv, get_missingcolor(opt->missingcolor, nchannels)); rgba_to_nchannels(rgba, nchannels, result); status = true; break; } case OSLTextureHandleType::IES: { if (nchannels > 0) { result[0] = kernel_ies_interp(kg, image_texture_or_udim_id, s, t); } status = true; break; } case OSLTextureHandleType::BEVEL: { #if !defined(__KERNEL_GPU__) && defined(__SHADER_RAYTRACE__) /* Bevel shader hack. */ ConstIntegratorState state = sg->path_state; if (nchannels >= 3 && state != nullptr) { const int num_samples = int(s); const float radius = t; const float3 N = svm_bevel(kg, state, sd, radius, num_samples); result[0] = N.x; result[1] = N.y; result[2] = N.z; status = true; } #else if (nchannels >= 3) { result[0] = sd->N.x; result[1] = sd->N.y; result[2] = sd->N.z; status = true; } #endif break; } case OSLTextureHandleType::AO: { #if !defined(__KERNEL_GPU__) && defined(__SHADER_RAYTRACE__) /* AO shader hack. */ ConstIntegratorState state = sg->path_state; if (state != nullptr) { const int num_samples = int(s); const float radius = t; const float3 N = make_float3(dsdx, dtdx, dsdy); int flags = 0; if (int(dtdy)) { flags |= NODE_AO_INSIDE; } if (opt->sblur) { flags |= NODE_AO_ONLY_LOCAL; } if (opt->tblur) { flags |= NODE_AO_GLOBAL_RADIUS; } result[0] = svm_ao(kg, state, sd, N, radius, num_samples, flags); status = true; } #else result[0] = 1.0f; status = true; #endif break; } } if (!status) { if (opt->missingcolor) { missingcolor_to_nchannels(opt->missingcolor, nchannels, result); } else { rgba_to_nchannels(IMAGE_MISSING_RGBA, nchannels, result); } } return status; } ccl_device bool osl_shared_texture3d(KernelGlobals kg, ccl_private ShaderGlobals *sg, ccl_private void *texture_handle, float3 P, float3 /*dPdx*/, float3 /*dPdy*/, float3 /*dPdz*/, int nchannels, float *result) { const OSLTextureHandleType type = OSL_TEXTURE_HANDLE_TYPE(texture_handle); const int image_texture_id = OSL_TEXTURE_HANDLE_ID(texture_handle); bool status = false; switch (type) { case OSLTextureHandleType::IMAGE: { const float4 rgba = kernel_image_interp_3d( kg, sg->sd, image_texture_id, P, INTERPOLATION_NONE, false); rgba_to_nchannels(rgba, nchannels, result); status = true; break; } default: break; } if (!status) { rgba_to_nchannels(IMAGE_MISSING_RGBA, nchannels, result); } return status; } ccl_device bool osl_shared_environment(KernelGlobals kg, ccl_private ShaderGlobals *sg, ccl_private void *texture_handle, ccl_private OSLTextureOptions *opt, float3 R, float3 dRdx, float3 dRdy, int nchannels, float *result) { const OSLTextureHandleType type = OSL_TEXTURE_HANDLE_TYPE(texture_handle); const int image_texture_or_udim_id = OSL_TEXTURE_HANDLE_ID(texture_handle); if (type == OSLTextureHandleType::IMAGE) { ccl_private ShaderData *sd = sg->sd; const dual3 R_dual(R, dRdx, dRdy); /* Environment call is always equirectangular. */ const dual2 uv(direction_to_equirectangular(R_dual.val)); const float4 rgba = kernel_image_interp_with_udim( kg, sd, image_texture_or_udim_id, uv, get_missingcolor(opt->missingcolor, nchannels)); rgba_to_nchannels(rgba, nchannels, result); return true; } if (opt->missingcolor) { missingcolor_to_nchannels(opt->missingcolor, nchannels, result); } else { rgba_to_nchannels(IMAGE_MISSING_RGBA, nchannels, result); } return false; } /* Scene Attributes */ ccl_device_inline bool osl_shared_get_scene_attribute(KernelGlobals kg, DeviceString name, const TypeDesc type, bool derivatives, ccl_private void *val) { if (name == DeviceStrings::u_scene_time) { return set_attribute(kernel_data.scene_time.time, type, derivatives, val); } if (name == DeviceStrings::u_scene_frame) { return set_attribute(kernel_data.scene_time.frame, type, derivatives, val); } return false; } /* Object Attribute Retrieval */ template ccl_device_inline bool osl_shared_get_object_attribute_impl(KernelGlobals kg, ccl_private ShaderData *sd, const AttributeDescriptor &desc, const TypeDesc type, bool derivatives, ccl_private void *val) { dual data; #ifdef __VOLUME__ if (primitive_is_volume_attribute(sd)) { data.val = primitive_volume_attribute(kg, sd, desc, true); } else #endif { if (derivatives) { data = primitive_surface_attribute>(kg, sd, desc); } else { data = dual(primitive_surface_attribute(kg, sd, desc)); } } return set_attribute(data, type, derivatives, val); } ccl_device_inline bool osl_shared_get_object_attribute(KernelGlobals kg, ccl_private ShaderData *sd, const AttributeDescriptor &desc, const TypeDesc type, bool derivatives, ccl_private void *val) { if (desc.type == NODE_ATTR_FLOAT) { return osl_shared_get_object_attribute_impl(kg, sd, desc, type, derivatives, val); } if (desc.type == NODE_ATTR_FLOAT2) { return osl_shared_get_object_attribute_impl(kg, sd, desc, type, derivatives, val); } if (desc.type == NODE_ATTR_FLOAT3) { return osl_shared_get_object_attribute_impl(kg, sd, desc, type, derivatives, val); } if (desc.type == NODE_ATTR_FLOAT4 || desc.type == NODE_ATTR_RGBA) { return osl_shared_get_object_attribute_impl(kg, sd, desc, type, derivatives, val); } if (desc.type == NODE_ATTR_QUATERNION) { return osl_shared_get_object_attribute_impl(kg, sd, desc, type, derivatives, val); } if (desc.type == NODE_ATTR_MATRIX) { const Transform tfm = primitive_attribute_matrix(kg, desc); return set_attribute_matrix(tfm, type, val); } return false; } /* Background Attributes */ ccl_device_inline bool osl_shared_get_background_attribute(KernelGlobals kg, ccl_private ShaderGlobals *sg, ccl_private ShaderData *sd, DeviceString name, const TypeDesc type, bool derivatives, ccl_private void *val) { #ifdef __KERNEL_GPU__ ConstIntegratorState state = (sg->shade_index > 0) ? (sg->shade_index - 1) : -1; ConstIntegratorShadowState shadow_state = (sg->shade_index < 0) ? (-sg->shade_index - 1) : -1; # define READ_PATH_STATE(elem) \ ((state != -1) ? INTEGRATOR_STATE(state, path, elem) : \ (shadow_state != -1) ? INTEGRATOR_STATE(shadow_state, shadow_path, elem) : \ 0) #else const IntegratorStateCPU *state = sg->path_state; const IntegratorShadowStateCPU *shadow_state = sg->shadow_path_state; # define READ_PATH_STATE(elem) \ ((state != nullptr) ? state->path.elem : \ (shadow_state != nullptr) ? shadow_state->shadow_path.elem : \ 0) #endif if (name == DeviceStrings::u_path_ray_length) { /* Ray Length */ const float f = sd->ray_length; return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_path_ray_depth) { /* Ray Depth */ int f = READ_PATH_STATE(bounce); /* Read bounce from different locations depending on if this is a shadow path. For background, * light emission and shadow evaluation from a surface or volume we are effectively one bounce * further. */ if ((OSL_RAYTYPE_TO_VISIBILITY(sg->raytype) & PATH_RAY_VISIBILITY_SHADOW) || (OSL_RAYTYPE_TO_PARTIAL_PATH_FLAG(sg->raytype) & PATH_RAY_EMISSION)) { f += 1; } return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_path_diffuse_depth) { /* Diffuse Ray Depth */ const int f = READ_PATH_STATE(diffuse_bounce); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_path_glossy_depth) { /* Glossy Ray Depth */ const int f = READ_PATH_STATE(glossy_bounce); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_path_transmission_depth) { /* Transmission Ray Depth */ const int f = READ_PATH_STATE(transmission_bounce); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_path_transparent_depth) { /* Transparent Ray Depth */ const int f = READ_PATH_STATE(transparent_bounce); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_path_portal_depth) { /* Portal Ray Depth */ const int f = READ_PATH_STATE(portal_bounce); return set_attribute(f, type, derivatives, val); } #undef READ_PATH_STATE if (name == DeviceStrings::u_ndc) { /* NDC coordinates with special exception for orthographic projection. */ dual3 ndc; if ((OSL_RAYTYPE_TO_VISIBILITY(sg->raytype) & PATH_RAY_VISIBILITY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) { ndc = dual3(camera_world_to_ndc(kg, sd, sd->ray_P)); } else { ndc = dual3(camera_world_to_ndc(kg, sd, sd->P)); if (derivatives) { const differential3 dP = differential_from_compact(sd->Ng, sd->dP); ndc.dx = camera_world_to_ndc(kg, sd, sd->P + dP.dx) - ndc.val; ndc.dy = camera_world_to_ndc(kg, sd, sd->P + dP.dy) - ndc.val; } } return set_attribute(ndc, type, derivatives, val); } return osl_shared_get_scene_attribute(kg, name, type, derivatives, val); } /* Object Standard Attributes */ ccl_device_inline bool osl_shared_get_object_standard_attribute(KernelGlobals kg, ccl_private ShaderGlobals *sg, ccl_private ShaderData *sd, DeviceString name, const TypeDesc type, bool derivatives, ccl_private void *val) { /* Object Attributes */ if (name == DeviceStrings::u_object_location) { const float3 f = object_location(kg, sd); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_object_color) { const float3 f = object_color(kg, sd->object); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_object_alpha) { const float f = object_alpha(kg, sd->object); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_object_index) { const float f = object_pass_id(kg, sd->object); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_object_is_light) { const float f = (sd->type & PRIMITIVE_LAMP) != 0; return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_geom_dupli_generated) { const float3 f = object_dupli_generated(kg, sd->object); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_geom_dupli_uv) { const float3 f = object_dupli_uv(kg, sd->object); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_material_index) { const float f = shader_pass_id(kg, sd); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_object_random) { const float f = object_random_number(kg, sd->object); return set_attribute(f, type, derivatives, val); } /* Particle Attributes */ if (name == DeviceStrings::u_particle_index) { const int particle_id = object_particle_id(kg, sd->object); const float f = particle_index(kg, particle_id); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_particle_random) { const int particle_id = object_particle_id(kg, sd->object); const float f = hash_uint2_to_float(particle_index(kg, particle_id), 0); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_particle_age) { const int particle_id = object_particle_id(kg, sd->object); const float f = particle_age(kg, particle_id); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_particle_lifetime) { const int particle_id = object_particle_id(kg, sd->object); const float f = particle_lifetime(kg, particle_id); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_particle_location) { const int particle_id = object_particle_id(kg, sd->object); const float3 f = particle_location(kg, particle_id); return set_attribute(f, type, derivatives, val); } #if 0 /* unsupported */ if (name == DeviceStrings::u_particle_rotation) { const int particle_id = object_particle_id(kg, sd->object); const float4 f = particle_rotation(kg, particle_id); return set_attribute(f, type, derivatives, val); } #endif if (name == DeviceStrings::u_particle_size) { const int particle_id = object_particle_id(kg, sd->object); const float f = particle_size(kg, particle_id); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_particle_velocity) { const int particle_id = object_particle_id(kg, sd->object); const float3 f = particle_velocity(kg, particle_id); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_particle_angular_velocity) { const int particle_id = object_particle_id(kg, sd->object); const float3 f = particle_angular_velocity(kg, particle_id); return set_attribute(f, type, derivatives, val); } /* Geometry Attributes */ if (name == DeviceStrings::u_geom_numpolyvertices) { return set_attribute(3, type, derivatives, val); } if ((name == DeviceStrings::u_geom_trianglevertices || name == DeviceStrings::u_geom_polyvertices) && sd->type & PRIMITIVE_TRIANGLE) { float3 P[3]; if (sd->type & PRIMITIVE_MOTION) { motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, P); } else { triangle_vertices(kg, sd->object, sd->prim, P); } if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { object_position_transform(kg, sd, &P[0]); object_position_transform(kg, sd, &P[1]); object_position_transform(kg, sd, &P[2]); } return set_attribute_float3_3(P, type, derivatives, val); } #ifndef __KERNEL_GPU__ if (name == DeviceStrings::u_geom_name) { const ustring object_name = kg->osl.globals->object_names[sd->object]; return set_attribute(object_name, type, derivatives, val); } #endif if (name == DeviceStrings::u_is_smooth) { const float f = ((sd->shader & SHADER_SMOOTH_NORMAL) != 0); return set_attribute(f, type, derivatives, val); } #ifdef __HAIR__ /* Hair Attributes */ if (name == DeviceStrings::u_is_curve) { const float f = (sd->type & PRIMITIVE_CURVE) != 0; return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_curve_thickness) { const float f = curve_thickness(kg, sd); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_curve_tangent_normal) { const float3 f = curve_tangent_normal(sd); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_curve_random) { const float f = curve_random(kg, sd); return set_attribute(f, type, derivatives, val); } #endif #ifdef __POINTCLOUD__ /* Point Attributes */ if (name == DeviceStrings::u_is_point) { const float f = (sd->type & PRIMITIVE_ANY_POINT) != 0; return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_point_radius) { const float f = point_radius(kg, sd); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_point_position) { const float3 f = point_position(kg, sd); return set_attribute(f, type, derivatives, val); } if (name == DeviceStrings::u_point_random) { const float f = point_random(kg, sd); return set_attribute(f, type, derivatives, val); } #endif if (name == DeviceStrings::u_normal_map_normal) { if (sd->type & PRIMITIVE_TRIANGLE) { const AttributeDescriptor desc = find_attribute( kg, sd->object, sd->prim, ATTR_STD_NORMAL_UNDISPLACED); if (is_attribute_found(desc)) { return osl_shared_get_object_attribute(kg, sd, desc, type, derivatives, val); } const float3 f = triangle_smooth_normal_unnormalized_object_space(kg, sd); return set_attribute(f, type, derivatives, val); } return false; } if (name == DeviceStrings::u_bump_map_normal) { dual3 f; if (!attribute_bump_map_normal(kg, sd, f)) { return false; } return set_attribute(f, type, derivatives, val); } #if defined(__GSPLATS__) /* Gaussian splats attributes. */ if (sd->type & PRIMITIVE_GSPLAT && name == DeviceStrings::u_geom_radiance) { const float3 radiance = gsplat_radiance(kg, *sd); return set_attribute(radiance, type, derivatives, val); } #endif return osl_shared_get_background_attribute(kg, sg, sd, name, type, derivatives, val); } /* Camera Attributes */ ccl_device_inline bool osl_shared_get_camera_attribute(KernelGlobals kg, ccl_private ShaderGlobals *sg, DeviceString name, const TypeDesc type, bool derivatives, ccl_private void *val) { if (name == DeviceStrings::u_sensor_size) { const float2 sensor = make_float2(kernel_data.cam.sensorwidth, kernel_data.cam.sensorheight); return set_attribute(sensor, type, derivatives, val); } if (name == DeviceStrings::u_image_resolution) { const float2 image = make_float2(kernel_data.cam.width, kernel_data.cam.height); return set_attribute(image, type, derivatives, val); } if (name == DeviceStrings::u_aperture_aspect_ratio) { return set_attribute(1.0f / kernel_data.cam.inv_aperture_ratio, type, derivatives, val); } if (name == DeviceStrings::u_aperture_size) { return set_attribute(kernel_data.cam.aperturesize, type, derivatives, val); } if (name == DeviceStrings::u_aperture_position) { /* The random numbers for aperture sampling are packed into N. */ const float2 rand_lens = make_float2(sg->N.x, sg->N.y); const float2 pos = camera_sample_aperture(&kernel_data.cam, rand_lens); return set_attribute(pos * kernel_data.cam.aperturesize, type, derivatives, val); } if (name == DeviceStrings::u_focal_distance) { return set_attribute(kernel_data.cam.focaldistance, type, derivatives, val); } return osl_shared_get_scene_attribute(kg, name, type, derivatives, val); } CCL_NAMESPACE_END