blender/intern/cycles/kernel/svm/attribute.h
Sergey Sharybin 071bea748b Fix: Cycles access to radiance attribute for gsplats
This commit fixes the behavior when the shader graph accesses "radiance"
attribute in the case when the geometry has such attribute. The desired
behavior is to output the stored attribute instead of doing the runtime
radiance+base + spherical harmonics evaluation.

This is how OSL backend behaved prior to this change. So in a way it
fixes discrepancy between SVM and OSL.

Pull Request: https://projects.blender.org/blender/blender/pulls/164039
2026-09-18 19:31:19 +02:00

296 lines
10 KiB
C++

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/gsplat.h"
#include "kernel/geom/object.h"
#include "kernel/geom/primitive.h"
#include "kernel/geom/volume.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Attribute Node */
ccl_device AttributeDescriptor svm_node_attr_init(KernelGlobals kg,
ccl_private ShaderData *sd,
const ccl_global SVMNodeAttr &ccl_restrict node,
ccl_private NodeAttributeOutputType *type)
{
*type = node.output_type;
AttributeDescriptor desc;
if (sd->object != OBJECT_NONE) {
desc = find_attribute(kg, sd, node.attr);
if (!is_attribute_found(desc)) {
desc = attribute_not_found();
desc.type = (NodeAttributeType)node.output_type;
}
}
else {
/* background */
desc = attribute_not_found();
desc.type = (NodeAttributeType)node.output_type;
}
return desc;
}
/* Store attribute to the stack. Float3Type is float3 or dual3. */
template<typename Float3Type>
ccl_device_inline void svm_node_attr_store(const NodeAttributeOutputType type,
ccl_private float *stack,
const uint out_offset,
const ccl_private Float3Type &f)
{
using FloatType = dual_scalar_t<Float3Type>;
if (type == NODE_ATTR_OUTPUT_FLOAT3) {
stack_store(stack, out_offset, f);
}
else {
stack_store(stack, out_offset, FloatType(average(f)));
}
}
/* Core surface attribute evaluation, returning Float3Type = float3 or dual3.
* Fetches the attribute, applies output type conversion (float3 or scalar-as-float3),
* and computes derivatives when Float3Type is a dual type. */
template<typename Float3Type>
ccl_device_inline Float3Type
svm_node_attr_surface_eval(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAttr &ccl_restrict node,
const NodeAttributeOutputType type,
const AttributeDescriptor desc)
{
using FloatType = dual_scalar_t<Float3Type>;
#if defined(__GSPLATS__)
if (sd->type & PRIMITIVE_GSPLAT && node.attr == ATTR_STD_GSPLAT_RADIANCE &&
!is_attribute_found(desc))
{
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(FloatType(1.0f));
}
const float3 radiance = gsplat_radiance(kg, *sd);
return Float3Type(radiance);
}
#endif
/* Spherical harmonics attribute can not be currently accessed.
* It is stored as PackedSphericalHarmonics that does not have a float or float3 representation.
*/
if (desc.type == NODE_ATTR_SPHERICAL_HARMONICS_REST) {
const float value = (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) ? 1.0f : 0.0f;
return make_float3(FloatType(value));
}
if (sd->type == PRIMITIVE_LAMP && node.attr == ATTR_STD_UV) {
Float3Type uv(make_float3(1.0f - sd->u - sd->v, sd->u, 0.0f));
if constexpr (is_dual_v<Float3Type>) {
uv.dx = make_float3(-sd->du.dx - sd->dv.dx, sd->du.dx, 0.0f);
uv.dy = make_float3(-sd->du.dy - sd->dv.dy, sd->du.dy, 0.0f);
}
return uv;
}
if (node.attr == ATTR_STD_GENERATED && !is_attribute_found(desc)) {
Float3Type f = shading_position<Float3Type>(sd);
object_inverse_position_transform_if_object(kg, sd, &f);
return f;
}
if (!is_attribute_found(desc)) {
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(FloatType(stack_load(stack, node.missing_alpha)));
}
return Float3Type(stack_load(stack, node.missing));
}
/* Surface attribute fetch with output type conversion. */
if (desc.type == NODE_ATTR_FLOAT) {
FloatType f = primitive_surface_attribute<FloatType>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(FloatType(1.0f));
}
return make_float3(f, f, f);
}
if (desc.type == NODE_ATTR_FLOAT2) {
if constexpr (is_dual_v<Float3Type>) {
dual2 f = primitive_surface_attribute<dual2>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(f.x());
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(FloatType(1.0f));
}
return make_float3(f);
}
else {
float2 f = primitive_surface_attribute<float2>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(f.x);
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(FloatType(1.0f));
}
return make_float3(f);
}
}
if (desc.type == NODE_ATTR_FLOAT4 || desc.type == NODE_ATTR_RGBA) {
if constexpr (is_dual_v<Float3Type>) {
dual4 f = primitive_surface_attribute<dual4>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(average(make_float3(f)));
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(f.w());
}
return make_float3(f);
}
else {
float4 f = primitive_surface_attribute<float4>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(average(make_float3(f)));
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(f.w);
}
return make_float3(f);
}
}
if (desc.type == NODE_ATTR_QUATERNION) {
/* Conversion from quaternion to RGB is not well defined.
* Follow the same logic as what it was when quaternions were stored as float4. */
if constexpr (is_dual_v<Float3Type>) {
const dual<Quaternion> q = primitive_surface_attribute<dual<Quaternion>>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(average(make_float3(q)));
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
/* Use make_float4, which is used for the non-dual case and swizzles components. */
return make_float3(
dual<float>(make_float4(q.val).w, make_float4(q.dx).w, make_float4(q.dy).w));
}
return make_float3(q);
}
else {
const Quaternion q = primitive_surface_attribute<Quaternion>(kg, sd, desc);
const float4 f = make_float4(q);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(average(make_float3(f)));
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(f.w);
}
return make_float3(f);
}
}
Float3Type f = primitive_surface_attribute<Float3Type>(kg, sd, desc);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
return make_float3(average(f));
}
if (type == NODE_ATTR_OUTPUT_FLOAT_ALPHA) {
return make_float3(FloatType(1.0f));
}
return f;
}
/* Surface attribute node. */
ccl_device_noinline void svm_node_attr_surface(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAttr &ccl_restrict node)
{
NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT;
const AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type);
float3 data = svm_node_attr_surface_eval<float3>(kg, sd, stack, node, type, desc);
svm_node_attr_store(type, stack, node.out_offset, data);
}
/* Evaluate surface attributes with derivatives and optional bump offset.
* Used for derivative tracking and bump mapping. */
ccl_device_noinline void svm_node_attr_derivative(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAttr &ccl_restrict node)
{
NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT;
const AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type);
dual3 data = svm_node_attr_surface_eval<dual3>(kg, sd, stack, node, type, desc);
if (node.bump_offset == NODE_BUMP_OFFSET_DX) {
data.val += data.dx * node.bump_filter_width;
}
else if (node.bump_offset == NODE_BUMP_OFFSET_DY) {
data.val += data.dy * node.bump_filter_width;
}
if (node.store_derivatives) {
svm_node_attr_store(type, stack, node.out_offset, data);
}
else {
svm_node_attr_store(type, stack, node.out_offset, float3(data.val));
}
}
#ifdef __VOLUME__
/* Volume attribute node. Volumes have no derivatives or bump. */
ccl_device_noinline void svm_node_attr_volume(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAttr &ccl_restrict node)
{
kernel_assert(primitive_is_volume_attribute(sd));
NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT;
const AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type);
if (!is_attribute_found(desc)) {
if (type == NODE_ATTR_OUTPUT_FLOAT) {
const float3 missing = stack_load(stack, node.missing);
stack_store_float(stack, node.out_offset, average(missing));
}
else if (type == NODE_ATTR_OUTPUT_FLOAT3) {
const float3 missing = stack_load(stack, node.missing);
stack_store_float3(stack, node.out_offset, missing);
}
else {
const float f = stack_load(stack, node.missing_alpha);
stack_store_float(stack, node.out_offset, f);
}
return;
}
const bool stochastic_sample = __float_as_uint(node.bump_filter_width);
const float4 value = volume_attribute_float4(kg, sd, desc, stochastic_sample);
if (type == NODE_ATTR_OUTPUT_FLOAT) {
stack_store_float(stack, node.out_offset, volume_attribute_value<float>(value));
}
else if (type == NODE_ATTR_OUTPUT_FLOAT3) {
stack_store_float3(stack, node.out_offset, volume_attribute_value<float3>(value));
}
else {
stack_store_float(stack, node.out_offset, volume_attribute_alpha(value));
}
}
#endif
CCL_NAMESPACE_END