mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
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
1348 lines
43 KiB
C++
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
|