blender/intern/cycles/kernel/osl/services_shared.h
Sergey Sharybin 819d52f3d2 GSplat: Initial rendering support for Cycles
Based on the "Stochastic ray tracing of transparent 3D Gaussians" paper
by Xin Sun et. al. The basic idea: perform stochastic intersection with
the Gaussian splat based on its transparency.

Gaussian splats are implemented as a dedicated primitive type, but it
shares the same layout for position and radius as points, so a lot of
existing functions (positions, attributes, etc) work for both points
and splats.

For the Embree and hardware intersection it is implemented as a custom
primitive type.

The choice of using bounding spheres mainly comes from a balance between
performance and memory usage. More ideal would be to use OBB, but it is
not supported for custom primitive types in Embree and GPU HW-RT on all
backends.

There is a known limitation that comes from the fact that the datasets
are trained in sRGB space and Cycles work in Linear space: areas with
low opacity and high radiance render noticeably differently from the
ground-truth implementation.

Ref #159470

Pull Request: https://projects.blender.org/blender/blender/pulls/163103
2026-09-16 16:52:10 +02:00

1348 lines
43 KiB
C++

/* 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<typename T>
ccl_device_inline bool set_attribute(const dual<T> 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<ccl_private float *>(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<ccl_private float *>(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<ccl_private float *>(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<Quaternion> data,
bool derivatives,
ccl_private void *val)
{
ccl_private float *fval = static_cast<ccl_private float *>(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<Quaternion> 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<typename T>
ccl_device_inline bool set_attribute(const T f,
const TypeDesc type,
bool derivatives,
ccl_private void *val)
{
return set_attribute(dual<T>(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<ccl_private int *>(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<OSLUStringHash *>(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<ccl_private float *>(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<ccl_private float *>(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<int *>(data);
res[0] = int(tex.width);
res[1] = int(tex.height);
return true;
}
if (is_type_float2(datatype)) {
float *res = static_cast<float *>(data);
res[0] = float(tex.width);
res[1] = float(tex.height);
return true;
}
if (is_type_int3(datatype)) {
int *res = static_cast<int *>(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<float *>(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<int *>(data) = channels;
return true;
}
}
else if (dataname == DeviceStrings::u_exists) {
if (datatype == TypeInt) {
*static_cast<int *>(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<typename T>
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<T> data;
#ifdef __VOLUME__
if (primitive_is_volume_attribute(sd)) {
data.val = primitive_volume_attribute<T>(kg, sd, desc, true);
}
else
#endif
{
if (derivatives) {
data = primitive_surface_attribute<dual<T>>(kg, sd, desc);
}
else {
data = dual<T>(primitive_surface_attribute<T>(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<float>(kg, sd, desc, type, derivatives, val);
}
if (desc.type == NODE_ATTR_FLOAT2) {
return osl_shared_get_object_attribute_impl<float2>(kg, sd, desc, type, derivatives, val);
}
if (desc.type == NODE_ATTR_FLOAT3) {
return osl_shared_get_object_attribute_impl<float3>(kg, sd, desc, type, derivatives, val);
}
if (desc.type == NODE_ATTR_FLOAT4 || desc.type == NODE_ATTR_RGBA) {
return osl_shared_get_object_attribute_impl<float4>(kg, sd, desc, type, derivatives, val);
}
if (desc.type == NODE_ATTR_QUATERNION) {
return osl_shared_get_object_attribute_impl<Quaternion>(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