Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/integrator/path_state.h"
#include "kernel/bvh/bvh.h"
#include "kernel/sample/mapping.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
#ifdef __SHADER_RAYTRACE__
# ifdef __KERNEL_OPTIX__
extern "C" __device__ float __direct_callable__svm_node_ao(
# else
ccl_device float svm_ao(
# endif
KernelGlobals kg,
ConstIntegratorState state,
ccl_private ShaderData *sd,
float3 N,
float max_dist,
const int num_samples,
const int flags)
{
if (flags & NODE_AO_GLOBAL_RADIUS) {
max_dist = kernel_data.integrator.ao_bounces_distance;
}
/* Early out if no sampling needed. */
if (max_dist <= 0.0f || num_samples < 1 || sd->object == OBJECT_NONE) {
return 1.0f;
}
/* Can't ray-trace from shaders like displacement, before BVH exists. */
if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) {
return 1.0f;
}
if (flags & NODE_AO_INSIDE) {
N = -N;
}
float3 T;
float3 B;
make_orthonormals(N, &T, &B);
/* TODO: support ray-tracing in shadow shader evaluation? */
RNGState rng_state;
path_state_rng_load(state, &rng_state);
int unoccluded = 0;
for (int sample = 0; sample < num_samples; sample++) {
const float2 rand_disk = path_branched_rng_2D(
kg, &rng_state, sample, num_samples, PRNG_SURFACE_AO);
const float2 d = sample_uniform_disk(rand_disk);
const float3 D = make_float3(d.x, d.y, safe_sqrtf(1.0f - dot(d, d)));
/* Create ray. */
Ray ray;
ray.P = sd->P;
ray.D = to_global(D, T, B, N);
ray.tmin = 0.0f;
ray.tmax = max_dist;
ray.time = sd->time;
ray.self.object = sd->object;
ray.self.prim = sd->prim;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();
if (flags & NODE_AO_ONLY_LOCAL) {
if (!scene_intersect_local(kg, &ray, nullptr, sd->object, nullptr, 0)) {
unoccluded++;
}
}
else {
if (!scene_intersect_shadow(kg, &ray, PATH_RAY_VISIBILITY_SHADOW_OPAQUE)) {
unoccluded++;
}
}
}
return ((float)unoccluded) / num_samples;
}
template<uint node_feature_mask, typename ConstIntegratorGenericState>
# if defined(__KERNEL_OPTIX__)
ccl_device_inline
# else
ccl_device_noinline
# endif
void
svm_node_ao(KernelGlobals kg,
ConstIntegratorGenericState state,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAmbientOcclusion &ccl_restrict node)
{
float ao = 1.0f;
IF_KERNEL_NODES_FEATURE(RAYTRACE)
{
float dist = stack_load(stack, node.dist);
float3 normal = stack_load_float3_default(stack, node.normal_offset, sd->N);
normal = safe_normalize(normal);
# ifdef __KERNEL_OPTIX__
ao = optixDirectCall<float>(0, kg, state, sd, normal, dist, node.samples, node.flags);
# else
ao = svm_ao(kg, state, sd, normal, dist, node.samples, node.flags);
# endif
}
if (stack_valid(node.out_ao_offset)) {
stack_store_float(stack, node.out_ao_offset, ao);
}
if (stack_valid(node.out_color_offset)) {
const float3 color = stack_load(stack, node.color);
stack_store_float3(stack, node.out_color_offset, ao * color);
}
}
#endif /* __SHADER_RAYTRACE__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,62 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/film/aov_passes.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline bool svm_node_aov_check(const uint32_t path_flag,
const ccl_global float *render_buffer)
{
const bool is_primary = (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) &&
(!(path_flag & PATH_RAY_SINGLE_PASS_DONE));
return ((render_buffer != nullptr) && is_primary);
}
template<uint node_feature_mask, typename ConstIntegratorGenericState>
ccl_device void svm_node_aov_color(KernelGlobals kg,
ccl_private ShaderData *sd,
ConstIntegratorGenericState state,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAOVColor &ccl_restrict node,
ccl_global float *render_buffer)
{
IF_KERNEL_NODES_FEATURE(AOV)
{
/* Don't write AOV on texture cache miss, we'll try again when the texture exists. */
if (sd->flag & SD_CACHE_MISS) {
return;
}
const float3 val = stack_load(stack, node.color);
film_write_aov_pass_color(kg, state, render_buffer, node.aov_offset, val);
}
}
template<uint node_feature_mask, typename ConstIntegratorGenericState>
ccl_device void svm_node_aov_value(KernelGlobals kg,
ccl_private ShaderData *sd,
ConstIntegratorGenericState state,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeAOVValue &ccl_restrict node,
ccl_global float *render_buffer)
{
IF_KERNEL_NODES_FEATURE(AOV)
{
/* Don't write AOV on texture cache miss, we'll try again when the texture exists. */
if (sd->flag & SD_CACHE_MISS) {
return;
}
const float val = stack_load(stack, node.value);
film_write_aov_pass_value(kg, state, render_buffer, node.aov_offset, val);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,224 @@
/* 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/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,
const ccl_global SVMNodeAttr &ccl_restrict node,
const NodeAttributeOutputType type,
const AttributeDescriptor desc)
{
using FloatType = dual_scalar_t<Float3Type>;
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;
}
/* 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);
}
}
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, 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, 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);
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

View File

@@ -0,0 +1,330 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/bvh/bvh.h"
#include "kernel/geom/motion_triangle.h"
#include "kernel/geom/triangle.h"
#include "kernel/geom/triangle_intersect.h"
#include "kernel/integrator/path_state.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
#ifdef __SHADER_RAYTRACE__
/* Planar Cubic BSSRDF falloff, reused for bevel.
*
* This is basically (Rm - x)^3, with some factors to normalize it. For sampling
* we integrate 2*pi*x * (Rm - x)^3, which gives us a quintic equation that as
* far as I can tell has no closed form solution. So we get an iterative solution
* instead with newton-raphson. */
ccl_device float svm_bevel_cubic_eval(const float radius, const float r)
{
const float Rm = radius;
if (r >= Rm) {
return 0.0f;
}
/* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */
const float Rm5 = (Rm * Rm) * (Rm * Rm) * Rm;
const float f = Rm - r;
const float num = f * f * f;
return (10.0f * num) / (Rm5 * M_PI_F);
}
ccl_device float svm_bevel_cubic_pdf(const float radius, const float r)
{
return svm_bevel_cubic_eval(radius, r);
}
/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */
ccl_device_forceinline float svm_bevel_cubic_quintic_root_find(const float xi)
{
/* newton-raphson iteration, usually succeeds in 2-4 iterations, except
* outside 0.02 ... 0.98 where it can go up to 10, so overall performance
* should not be too bad */
const float tolerance = 1e-6f;
const int max_iteration_count = 10;
float x = 0.25f;
int i;
for (i = 0; i < max_iteration_count; i++) {
const float x2 = x * x;
const float x3 = x2 * x;
const float nx = (1.0f - x);
const float f = 10.0f * x2 - 20.0f * x3 + 15.0f * x2 * x2 - 4.0f * x2 * x3 - xi;
const float f_ = 20.0f * (x * nx) * (nx * nx);
if (fabsf(f) < tolerance || f_ == 0.0f) {
break;
}
x = saturatef(x - f / f_);
}
return x;
}
ccl_device void svm_bevel_cubic_sample(const float radius,
const float xi,
ccl_private float *r,
ccl_private float *h)
{
const float Rm = radius;
float r_ = svm_bevel_cubic_quintic_root_find(xi);
r_ *= Rm;
*r = r_;
/* h^2 + r^2 = Rm^2 */
*h = safe_sqrtf(Rm * Rm - r_ * r_);
}
/* Bevel shader averaging normals from nearby surfaces.
*
* Sampling strategy from: BSSRDF Importance Sampling, SIGGRAPH 2013
* http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf
*/
# ifdef __KERNEL_OPTIX__
extern "C" __device__ float3 __direct_callable__svm_node_bevel(
# else
ccl_device float3 svm_bevel(
# endif
KernelGlobals kg,
ConstIntegratorState state,
ccl_private ShaderData *sd,
const float radius,
const int num_samples)
{
/* Early out if no sampling needed. */
if (radius <= 0.0f || num_samples < 1 || sd->object == OBJECT_NONE) {
return sd->N;
}
/* Can't ray-trace from shaders like displacement, before BVH exists. */
if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) {
return sd->N;
}
/* Don't bevel for blurry indirect rays. */
if (INTEGRATOR_STATE(state, path, min_ray_pdf) < 8.0f) {
return sd->N;
}
/* Setup for multi intersection. */
LocalIntersection isect;
uint lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_pixel),
INTEGRATOR_STATE(state, path, rng_offset),
INTEGRATOR_STATE(state, path, sample),
0x64c6a40e);
/* Sample normals from surrounding points on surface. */
float3 sum_N = make_float3(0.0f, 0.0f, 0.0f);
/* TODO: support ray-tracing in shadow shader evaluation? */
RNGState rng_state;
path_state_rng_load(state, &rng_state);
for (int sample = 0; sample < num_samples; sample++) {
float2 rand_disk = path_branched_rng_2D(
kg, &rng_state, sample, num_samples, PRNG_SURFACE_BEVEL);
/* Pick random axis in local frame and point on disk. */
float3 disk_N;
float3 disk_T;
float3 disk_B;
float pick_pdf_N;
float pick_pdf_T;
float pick_pdf_B;
disk_N = sd->Ng;
make_orthonormals(disk_N, &disk_T, &disk_B);
const float axisu = rand_disk.x;
if (axisu < 0.5f) {
pick_pdf_N = 0.5f;
pick_pdf_T = 0.25f;
pick_pdf_B = 0.25f;
rand_disk.x *= 2.0f;
}
else if (axisu < 0.75f) {
const float3 tmp = disk_N;
disk_N = disk_T;
disk_T = tmp;
pick_pdf_N = 0.25f;
pick_pdf_T = 0.5f;
pick_pdf_B = 0.25f;
rand_disk.x = (rand_disk.x - 0.5f) * 4.0f;
}
else {
const float3 tmp = disk_N;
disk_N = disk_B;
disk_B = tmp;
pick_pdf_N = 0.25f;
pick_pdf_T = 0.25f;
pick_pdf_B = 0.5f;
rand_disk.x = (rand_disk.x - 0.75f) * 4.0f;
}
/* Sample point on disk. */
const float phi = M_2PI_F * rand_disk.x;
float disk_r = rand_disk.y;
float disk_height;
/* Perhaps find something better than Cubic BSSRDF, but happens to work well. */
svm_bevel_cubic_sample(radius, disk_r, &disk_r, &disk_height);
const float3 disk_P = to_global(polar_to_cartesian(disk_r, phi), disk_T, disk_B);
/* Create ray. */
Ray ray ccl_optional_struct_init;
ray.P = sd->P + disk_N * disk_height + disk_P;
ray.D = -disk_N;
ray.tmin = 0.0f;
ray.tmax = 2.0f * disk_height;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();
ray.time = sd->time;
ray.self.object = OBJECT_NONE;
ray.self.prim = PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
/* Intersect with the same object. if multiple intersections are found it
* will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */
scene_intersect_local(kg, &ray, &isect, sd->object, &lcg_state, LOCAL_MAX_HITS);
const int num_eval_hits = min(isect.num_hits, LOCAL_MAX_HITS);
for (int hit = 0; hit < num_eval_hits; hit++) {
/* Quickly retrieve P and Ng without setting up ShaderData. */
float3 hit_P;
if (sd->type == PRIMITIVE_TRIANGLE) {
hit_P = triangle_point_from_uv(
kg, sd, isect.hits[hit].prim, isect.hits[hit].u, isect.hits[hit].v);
}
# ifdef __OBJECT_MOTION__
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
float3 verts[3];
motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts);
hit_P = triangle_point_from_uv_and_verts(
kg, sd, isect.hits[hit].u, isect.hits[hit].v, verts);
}
# endif /* __OBJECT_MOTION__ */
/* Get geometric normal. */
float3 hit_Ng = isect.Ng[hit];
const int object = isect.hits[hit].object;
const uint object_flag = kernel_data_fetch(object_flag, object);
if (object_negative_scale_applied(object_flag)) {
hit_Ng = -hit_Ng;
}
/* Compute smooth normal. */
float3 N = hit_Ng;
const int prim = isect.hits[hit].prim;
const int shader = kernel_data_fetch(tri_shader, prim);
if (shader & SHADER_SMOOTH_NORMAL) {
const float u = isect.hits[hit].u;
const float v = isect.hits[hit].v;
if (sd->type == PRIMITIVE_TRIANGLE) {
N = triangle_smooth_normal(kg, N, object, object_flag, prim, u, v);
}
# ifdef __OBJECT_MOTION__
else if (sd->type == PRIMITIVE_MOTION_TRIANGLE) {
N = motion_triangle_smooth_normal(kg, N, object, prim, u, v, sd->time);
}
# endif /* __OBJECT_MOTION__ */
}
/* Transform normals to world space. */
if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_normal_transform(kg, sd, &N);
object_normal_transform(kg, sd, &hit_Ng);
}
/* Probability densities for local frame axes. */
const float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng));
const float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng));
const float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng));
/* Multiple importance sample between 3 axes, power heuristic
* found to be slightly better than balance heuristic. pdf_N
* in the MIS weight and denominator canceled out. */
float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B));
if (isect.num_hits > LOCAL_MAX_HITS) {
w *= isect.num_hits / (float)LOCAL_MAX_HITS;
}
/* Real distance to sampled point. */
const float r = len(hit_P - sd->P);
/* Compute weight. */
const float pdf = svm_bevel_cubic_pdf(radius, r);
const float disk_pdf = svm_bevel_cubic_pdf(radius, disk_r);
w *= pdf / disk_pdf;
/* Sum normal and weight. */
sum_N += w * N;
}
}
/* Normalize. */
const float3 N = safe_normalize(sum_N);
return is_zero(N) ? sd->N : (sd->flag & SD_BACKFACING) ? -N : N;
}
template<uint node_feature_mask, typename ConstIntegratorGenericState>
# if defined(__KERNEL_OPTIX__)
ccl_device_inline
# else
ccl_device_noinline
# endif
void
svm_node_bevel(KernelGlobals kg,
ConstIntegratorGenericState state,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeBevel &ccl_restrict node)
{
float3 bevel_N = sd->N;
IF_KERNEL_NODES_FEATURE(RAYTRACE)
{
float radius = stack_load(stack, node.radius);
# ifdef __KERNEL_OPTIX__
bevel_N = optixDirectCall<float3>(1, kg, state, sd, radius, node.num_samples);
# else
bevel_N = svm_bevel(kg, state, sd, radius, node.num_samples);
# endif
if (stack_valid(node.normal_offset)) {
/* Preserve input normal. */
const float3 ref_N = stack_load_float3(stack, node.normal_offset);
bevel_N = normalize(ref_N + (bevel_N - sd->N));
}
}
stack_store_float3(stack, node.out_offset, bevel_N);
}
#endif /* __SHADER_RAYTRACE__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Adapted code from Open Shading Language. */
#pragma once
#include "kernel/globals.h"
#include "kernel/svm/math_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/colorspace.h"
CCL_NAMESPACE_BEGIN
/* Blackbody Node */
ccl_device_noinline void svm_node_blackbody(KernelGlobals kg,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeBlackbody &ccl_restrict node)
{
/* Input */
const float temperature = stack_load(stack, node.temperature);
float3 color_rgb = rec709_to_rgb(kg, svm_math_blackbody_color_rec709(temperature));
color_rgb = max(color_rgb, zero_float3());
stack_store_float3(stack, node.color_offset, color_rgb);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,113 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Brick */
ccl_device_inline float brick_noise(uint n) /* fast integer noise */
{
uint nn;
n = (n + 1013) & 0x7fffffff;
n = (n >> 13) ^ n;
nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return 0.5f * ((float)nn / 1073741824.0f);
}
ccl_device_noinline_cpu float2 svm_brick(const float3 p,
const float mortar_size,
const float mortar_smooth,
const float bias,
float brick_width,
const float row_height,
const float offset_amount,
const int offset_frequency,
const float squash_amount,
const int squash_frequency)
{
int bricknum;
int rownum;
float offset = 0.0f;
float x;
float y;
rownum = floor_to_int(p.y / row_height);
if (offset_frequency && squash_frequency) {
brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount; /* squash */
offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount); /* offset */
}
bricknum = floor_to_int((p.x + offset) / brick_width);
x = (p.x + offset) - brick_width * bricknum;
y = p.y - row_height * rownum;
const float tint = saturatef((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias));
float min_dist = min(min(x, y), min(brick_width - x, row_height - y));
float mortar;
if (min_dist >= mortar_size) {
mortar = 0.0f;
}
else if (mortar_smooth == 0.0f) {
mortar = 1.0f;
}
else {
min_dist = 1.0f - min_dist / mortar_size;
mortar = smoothstepf(min_dist / mortar_smooth);
}
return make_float2(tint, mortar);
}
ccl_device_noinline void svm_node_tex_brick(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexBrick &ccl_restrict node)
{
const float3 co = stack_load_float3(stack, node.co);
float3 color1 = stack_load(stack, node.color1);
const float3 color2 = stack_load(stack, node.color2);
const float3 mortar = stack_load(stack, node.mortar);
const float scale = stack_load(stack, node.scale);
const float mortar_size = stack_load(stack, node.mortar_size);
const float mortar_smooth = stack_load(stack, node.mortar_smooth);
const float bias = stack_load(stack, node.bias);
const float brick_width = stack_load(stack, node.brick_width);
const float row_height = stack_load(stack, node.row_height);
const float2 f2 = svm_brick(co * scale,
mortar_size,
mortar_smooth,
bias,
brick_width,
row_height,
node.offset_amount,
node.offset_frequency,
node.squash_amount,
node.squash_frequency);
const float tint = f2.x;
const float f = f2.y;
if (f != 1.0f) {
const float facm = 1.0f - tint;
color1 = facm * color1 + tint * color2;
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, color1 * (1.0f - f) + mortar * f);
}
if (stack_valid(node.fac_offset)) {
stack_store_float(stack, node.fac_offset, f);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,28 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/color_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_brightness(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeBrightContrast &ccl_restrict
node)
{
float3 color = stack_load(stack, node.color);
const float brightness = stack_load(stack, node.bright);
const float contrast = stack_load(stack, node.contrast);
color = svm_brightness_contrast(color, brightness, contrast);
if (stack_valid(node.out_offset)) {
stack_store_float3(stack, node.out_offset, color);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,63 @@
/* 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/object.h"
#include "kernel/geom/primitive.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/differential.h"
CCL_NAMESPACE_BEGIN
/* Bump Eval Nodes */
ccl_device_noinline void svm_node_enter_bump_eval(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeEnterBumpEval &node)
{
const uint offset = node.state_offset;
/* save state */
stack_store_float3(stack, offset + 0, sd->P);
stack_store_float(stack, offset + 3, sd->dP);
/* Set position as if undisplaced. */
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED);
if (is_attribute_found(desc)) {
dual3 attr = primitive_surface_attribute<dual3>(kg, sd, desc);
object_position_transform(kg, sd, &attr);
sd->P = attr.val;
sd->dP = differential_make_compact(attr);
/* Save the full differential, the compact form isn't enough for svm_node_set_bump. */
stack_store_float3(stack, offset + 4, attr.dx);
stack_store_float3(stack, offset + 7, attr.dy);
/* Set normal as if undisplaced. Note this does not need to be restored,
* because the bump evaluation will write to sd->N. */
primitive_normal_set_undisplaced(kg, sd, desc.offset);
}
}
ccl_device_noinline void svm_node_leave_bump_eval(ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeLeaveBumpEval &node)
{
const uint offset = node.state_offset;
/* restore state */
sd->P = stack_load_float3(stack, offset + 0);
sd->dP = stack_load_float(stack, offset + 3);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,37 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_camera(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeCamera &ccl_restrict node)
{
const Transform tfm = kernel_data.cam.worldtocamera;
const float3 vector = transform_point(&tfm, sd->P);
const float zdepth = vector.z;
const float distance = len(vector);
if (stack_valid(node.vector_offset)) {
stack_store_float3(stack, node.vector_offset, normalize(vector));
}
if (stack_valid(node.zdepth_offset)) {
stack_store_float(stack, node.zdepth_offset, zdepth);
}
if (stack_valid(node.distance_offset)) {
stack_store_float(stack, node.distance_offset, distance);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,46 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Checker */
ccl_device float svm_checker(float3 p)
{
/* avoid precision issues on unit coordinates */
p.x = (p.x + 0.000001f) * 0.999999f;
p.y = (p.y + 0.000001f) * 0.999999f;
p.z = (p.z + 0.000001f) * 0.999999f;
const int xi = abs(float_to_int(floorf(p.x)));
const int yi = abs(float_to_int(floorf(p.y)));
const int zi = abs(float_to_int(floorf(p.z)));
return ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f;
}
ccl_device_noinline void svm_node_tex_checker(
ccl_private float *ccl_restrict stack, const ccl_global SVMNodeTexChecker &ccl_restrict node)
{
const float3 co = stack_load_float3(stack, node.co);
const float3 color1 = stack_load(stack, node.color1);
const float3 color2 = stack_load(stack, node.color2);
const float scale = stack_load(stack, node.scale);
const float f = svm_checker(co * scale);
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, (f == 1.0f) ? color1 : color2);
}
if (stack_valid(node.fac_offset)) {
stack_store_float(stack, node.fac_offset, f);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,29 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Clamp Node */
ccl_device_noinline void svm_node_clamp(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeClamp &ccl_restrict node)
{
const float value = stack_load(stack, node.value);
const float min = stack_load(stack, node.min);
const float max = stack_load(stack, node.max);
if (node.clamp_type == NODE_CLAMP_RANGE && (min > max)) {
stack_store_float(stack, node.result_offset, clamp(value, max, min));
}
else {
stack_store_float(stack, node.result_offset, clamp(value, min, max));
}
}
CCL_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,409 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/types.h"
#include "util/color.h"
CCL_NAMESPACE_BEGIN
ccl_device float3 svm_mix_blend(const float t, const float3 col1, const float3 col2)
{
return interp(col1, col2, t);
}
ccl_device float3 svm_mix_add(const float t, const float3 col1, const float3 col2)
{
return interp(col1, col1 + col2, t);
}
ccl_device float3 svm_mix_mul(const float t, const float3 col1, const float3 col2)
{
return interp(col1, col1 * col2, t);
}
ccl_device float3 svm_mix_screen(const float t, const float3 col1, const float3 col2)
{
const float tm = 1.0f - t;
const float3 one = make_float3(1.0f, 1.0f, 1.0f);
const float3 tm3 = make_float3(tm, tm, tm);
return one - (tm3 + t * (one - col2)) * (one - col1);
}
ccl_device float3 svm_mix_overlay(const float t, const float3 col1, const float3 col2)
{
const float tm = 1.0f - t;
float3 outcol = col1;
if (outcol.x < 0.5f) {
outcol.x *= tm + 2.0f * t * col2.x;
}
else {
outcol.x = 1.0f - (tm + 2.0f * t * (1.0f - col2.x)) * (1.0f - outcol.x);
}
if (outcol.y < 0.5f) {
outcol.y *= tm + 2.0f * t * col2.y;
}
else {
outcol.y = 1.0f - (tm + 2.0f * t * (1.0f - col2.y)) * (1.0f - outcol.y);
}
if (outcol.z < 0.5f) {
outcol.z *= tm + 2.0f * t * col2.z;
}
else {
outcol.z = 1.0f - (tm + 2.0f * t * (1.0f - col2.z)) * (1.0f - outcol.z);
}
return outcol;
}
ccl_device float3 svm_mix_sub(const float t, const float3 col1, const float3 col2)
{
return interp(col1, col1 - col2, t);
}
ccl_device float3 svm_mix_div(const float t, const float3 col1, const float3 col2)
{
const float tm = 1.0f - t;
float3 outcol = col1;
if (col2.x != 0.0f) {
outcol.x = tm * outcol.x + t * outcol.x / col2.x;
}
if (col2.y != 0.0f) {
outcol.y = tm * outcol.y + t * outcol.y / col2.y;
}
if (col2.z != 0.0f) {
outcol.z = tm * outcol.z + t * outcol.z / col2.z;
}
return outcol;
}
ccl_device float3 svm_mix_diff(const float t, const float3 col1, const float3 col2)
{
return interp(col1, fabs(col1 - col2), t);
}
ccl_device float3 svm_mix_exclusion(const float t, const float3 col1, const float3 col2)
{
return max(interp(col1, col1 + col2 - 2.0f * col1 * col2, t), zero_float3());
}
ccl_device float3 svm_mix_dark(const float t, const float3 col1, const float3 col2)
{
return interp(col1, min(col1, col2), t);
}
ccl_device float3 svm_mix_light(const float t, const float3 col1, const float3 col2)
{
return interp(col1, max(col1, col2), t);
}
ccl_device float3 svm_mix_dodge(const float t, const float3 col1, const float3 col2)
{
float3 outcol = col1;
if (outcol.x != 0.0f) {
float tmp = 1.0f - t * col2.x;
if (tmp <= 0.0f) {
outcol.x = 1.0f;
}
else {
tmp = outcol.x / tmp;
if (tmp > 1.0f) {
outcol.x = 1.0f;
}
else {
outcol.x = tmp;
}
}
}
if (outcol.y != 0.0f) {
float tmp = 1.0f - t * col2.y;
if (tmp <= 0.0f) {
outcol.y = 1.0f;
}
else {
tmp = outcol.y / tmp;
if (tmp > 1.0f) {
outcol.y = 1.0f;
}
else {
outcol.y = tmp;
}
}
}
if (outcol.z != 0.0f) {
float tmp = 1.0f - t * col2.z;
if (tmp <= 0.0f) {
outcol.z = 1.0f;
}
else {
tmp = outcol.z / tmp;
if (tmp > 1.0f) {
outcol.z = 1.0f;
}
else {
outcol.z = tmp;
}
}
}
return outcol;
}
ccl_device float3 svm_mix_burn(const float t, const float3 col1, const float3 col2)
{
float tmp;
const float tm = 1.0f - t;
float3 outcol = col1;
tmp = tm + t * col2.x;
if (tmp <= 0.0f) {
outcol.x = 0.0f;
}
else {
tmp = (1.0f - (1.0f - outcol.x) / tmp);
if (tmp < 0.0f) {
outcol.x = 0.0f;
}
else if (tmp > 1.0f) {
outcol.x = 1.0f;
}
else {
outcol.x = tmp;
}
}
tmp = tm + t * col2.y;
if (tmp <= 0.0f) {
outcol.y = 0.0f;
}
else {
tmp = (1.0f - (1.0f - outcol.y) / tmp);
if (tmp < 0.0f) {
outcol.y = 0.0f;
}
else if (tmp > 1.0f) {
outcol.y = 1.0f;
}
else {
outcol.y = tmp;
}
}
tmp = tm + t * col2.z;
if (tmp <= 0.0f) {
outcol.z = 0.0f;
}
else {
tmp = (1.0f - (1.0f - outcol.z) / tmp);
if (tmp < 0.0f) {
outcol.z = 0.0f;
}
else if (tmp > 1.0f) {
outcol.z = 1.0f;
}
else {
outcol.z = tmp;
}
}
return outcol;
}
ccl_device float3 svm_mix_hue(const float t, const float3 col1, const float3 col2)
{
float3 outcol = col1;
const float3 hsv2 = rgb_to_hsv(col2);
if (hsv2.y != 0.0f) {
float3 hsv = rgb_to_hsv(outcol);
hsv.x = hsv2.x;
const float3 tmp = hsv_to_rgb(hsv);
outcol = interp(outcol, tmp, t);
}
return outcol;
}
ccl_device float3 svm_mix_sat(const float t, const float3 col1, const float3 col2)
{
const float tm = 1.0f - t;
float3 outcol = col1;
float3 hsv = rgb_to_hsv(outcol);
if (hsv.y != 0.0f) {
const float3 hsv2 = rgb_to_hsv(col2);
hsv.y = tm * hsv.y + t * hsv2.y;
outcol = hsv_to_rgb(hsv);
}
return outcol;
}
ccl_device float3 svm_mix_val(const float t, const float3 col1, const float3 col2)
{
const float tm = 1.0f - t;
float3 hsv = rgb_to_hsv(col1);
const float3 hsv2 = rgb_to_hsv(col2);
hsv.z = tm * hsv.z + t * hsv2.z;
return hsv_to_rgb(hsv);
}
ccl_device float3 svm_mix_color(const float t, const float3 col1, const float3 col2)
{
float3 outcol = col1;
const float3 hsv2 = rgb_to_hsv(col2);
if (hsv2.y != 0.0f) {
float3 hsv = rgb_to_hsv(outcol);
hsv.x = hsv2.x;
hsv.y = hsv2.y;
const float3 tmp = hsv_to_rgb(hsv);
outcol = interp(outcol, tmp, t);
}
return outcol;
}
ccl_device float3 svm_mix_soft(const float t, const float3 col1, const float3 col2)
{
const float tm = 1.0f - t;
const float3 one = make_float3(1.0f, 1.0f, 1.0f);
const float3 scr = one - (one - col2) * (one - col1);
return tm * col1 + t * ((one - col1) * col2 * col1 + col1 * scr);
}
ccl_device float3 svm_mix_linear(const float t, const float3 col1, const float3 col2)
{
return col1 + t * (2.0f * col2 + make_float3(-1.0f, -1.0f, -1.0f));
}
ccl_device float3 svm_mix_clamp(const float3 col)
{
return saturate(col);
}
ccl_device_noinline_cpu float3 svm_mix(NodeMix type,
const float t,
const float3 c1,
const float3 c2)
{
switch (type) {
case NODE_MIX_BLEND:
return svm_mix_blend(t, c1, c2);
case NODE_MIX_ADD:
return svm_mix_add(t, c1, c2);
case NODE_MIX_MUL:
return svm_mix_mul(t, c1, c2);
case NODE_MIX_SCREEN:
return svm_mix_screen(t, c1, c2);
case NODE_MIX_OVERLAY:
return svm_mix_overlay(t, c1, c2);
case NODE_MIX_SUB:
return svm_mix_sub(t, c1, c2);
case NODE_MIX_DIV:
return svm_mix_div(t, c1, c2);
case NODE_MIX_DIFF:
return svm_mix_diff(t, c1, c2);
case NODE_MIX_EXCLUSION:
return svm_mix_exclusion(t, c1, c2);
case NODE_MIX_DARK:
return svm_mix_dark(t, c1, c2);
case NODE_MIX_LIGHT:
return svm_mix_light(t, c1, c2);
case NODE_MIX_DODGE:
return svm_mix_dodge(t, c1, c2);
case NODE_MIX_BURN:
return svm_mix_burn(t, c1, c2);
case NODE_MIX_HUE:
return svm_mix_hue(t, c1, c2);
case NODE_MIX_SAT:
return svm_mix_sat(t, c1, c2);
case NODE_MIX_VAL:
return svm_mix_val(t, c1, c2);
case NODE_MIX_COL:
return svm_mix_color(t, c1, c2);
case NODE_MIX_SOFT:
return svm_mix_soft(t, c1, c2);
case NODE_MIX_LINEAR:
return svm_mix_linear(t, c1, c2);
case NODE_MIX_CLAMP:
return svm_mix_clamp(c1);
}
return make_float3(0.0f, 0.0f, 0.0f);
}
ccl_device_noinline_cpu float3 svm_mix_clamped_factor(NodeMix type,
const float t,
const float3 c1,
const float3 c2)
{
const float fac = saturatef(t);
return svm_mix(type, fac, c1, c2);
}
ccl_device_inline float3 svm_brightness_contrast(float3 color,
const float brightness,
const float contrast)
{
const float a = 1.0f + contrast;
const float b = brightness - contrast * 0.5f;
color.x = max(a * color.x + b, 0.0f);
color.y = max(a * color.y + b, 0.0f);
color.z = max(a * color.z + b, 0.0f);
return color;
}
ccl_device float3 svm_combine_color(NodeCombSepColorType type, const float3 color)
{
switch (type) {
case NODE_COMBSEP_COLOR_HSV:
return hsv_to_rgb(color);
case NODE_COMBSEP_COLOR_HSL:
return hsl_to_rgb(color);
case NODE_COMBSEP_COLOR_RGB:
default:
return color;
}
}
ccl_device float3 svm_separate_color(NodeCombSepColorType type, const float3 color)
{
switch (type) {
case NODE_COMBSEP_COLOR_HSV:
return rgb_to_hsv(color);
case NODE_COMBSEP_COLOR_HSL:
return rgb_to_hsl(color);
case NODE_COMBSEP_COLOR_RGB:
default:
return color;
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,72 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/colorspace.h"
CCL_NAMESPACE_BEGIN
/* Conversion Nodes */
template<typename FloatType, typename Float3Type>
ccl_device_noinline void svm_node_convert(KernelGlobals kg,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeConvert &ccl_restrict node)
{
switch (node.convert_type) {
case NODE_CONVERT_FI: {
/* TODO(weizhen): should actually store 0 for int, but none of the nodes that we compute
* derivatives for has int inputs, so seems fine. */
const float f = stack_load_float(stack, node.from_offset);
stack_store_int(stack, node.to_offset, float_to_int(f));
break;
}
case NODE_CONVERT_FV: {
const FloatType f = stack_load<FloatType>(stack, node.from_offset);
stack_store(stack, node.to_offset, make_float3(f, f, f));
break;
}
case NODE_CONVERT_CF: {
const Float3Type f = stack_load<Float3Type>(stack, node.from_offset);
stack_store(stack, node.to_offset, linear_rgb_to_gray(kg, f));
break;
}
case NODE_CONVERT_CI: {
const float3 f = stack_load_float3(stack, node.from_offset);
const int i = (int)linear_rgb_to_gray(kg, f);
stack_store_int(stack, node.to_offset, i);
break;
}
case NODE_CONVERT_VF: {
const Float3Type f = stack_load<Float3Type>(stack, node.from_offset);
stack_store(stack, node.to_offset, average(f));
break;
}
case NODE_CONVERT_VI: {
const float3 f = stack_load_float3(stack, node.from_offset);
const int i = (int)average(f);
stack_store_int(stack, node.to_offset, i);
break;
}
case NODE_CONVERT_IF: {
const float f = (float)stack_load_int(stack, node.from_offset);
stack_store(stack, node.to_offset, FloatType(f));
break;
}
case NODE_CONVERT_IV: {
const float f = (float)stack_load_int(stack, node.from_offset);
stack_store(stack, node.to_offset, Float3Type(make_float3(f, f, f)));
break;
}
default:
assert(false);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,197 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/geom/attribute.h"
#include "kernel/geom/object.h"
#include "kernel/geom/primitive.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/differential.h"
CCL_NAMESPACE_BEGIN
/* Bump Node */
template<uint node_feature_mask>
ccl_device_noinline void svm_node_set_bump(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeSetBump &node)
{
#ifdef __RAY_DIFFERENTIALS__
IF_KERNEL_NODES_FEATURE(BUMP)
{
/* get normal input */
float3 normal_in = stack_load_float3_default(stack, node.normal_offset, sd->N);
/* If we have saved bump state, read the full differential from there.
* Just using the compact form in those cases leads to incorrect normals (see #111588). */
differential3 dP;
if (node.bump_state_offset == SVM_STACK_INVALID) {
dP = differential_from_compact(sd->Ng, sd->dP);
}
else {
dP.dx = stack_load_float3(stack, node.bump_state_offset + 4);
dP.dy = stack_load_float3(stack, node.bump_state_offset + 7);
}
if (node.use_object_space) {
object_inverse_normal_transform(kg, sd, &normal_in);
object_inverse_dir_transform(kg, sd, &dP.dx);
object_inverse_dir_transform(kg, sd, &dP.dy);
}
/* get surface tangents from normal */
const float3 Rx = cross(dP.dy, normal_in);
const float3 Ry = cross(normal_in, dP.dx);
/* get bump values */
const float h_c = stack_load_float(stack, node.center_offset);
const float h_x = stack_load_float(stack, node.dx_offset);
const float h_y = stack_load_float(stack, node.dy_offset);
/* compute surface gradient and determinant */
const float det = dot(dP.dx, Rx);
const float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry;
const float absdet = fabsf(det);
float strength = stack_load(stack, node.strength);
float scale = stack_load(stack, node.scale);
if (node.invert) {
scale *= -1.0f;
}
strength = max(strength, 0.0f);
/* Compute and output perturbed normal.
* dP'dx = dPdx + scale * (h_x - h_c) / filter_width * normal
* dP'dy = dPdy + scale * (h_y - h_c) / filter_width * normal
* N' = cross(dP'dx, dP'dy)
* = cross(dPdx, dPdy) - scale * ((h_y - h_c) / filter_width * Ry + (h_x - h_c) /
* filter_width * Rx) ≈ det * normal_in - scale * surfgrad / filter_width
*/
float3 normal_out = safe_normalize(node.bump_filter_width * absdet * normal_in -
scale * signf(det) * surfgrad);
if (is_zero(normal_out)) {
normal_out = normal_in;
}
else {
normal_out = normalize(strength * normal_out + (1.0f - strength) * normal_in);
}
if (node.use_object_space) {
object_normal_transform(kg, sd, &normal_out);
}
stack_store_float3(stack, node.out_offset, normal_out);
}
else {
stack_store_float3(stack, node.out_offset, zero_float3());
}
#endif
}
/* Displacement Node */
template<uint node_feature_mask>
ccl_device void svm_node_set_displacement(ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeSetDisplacement &node)
{
IF_KERNEL_NODES_FEATURE(BUMP)
{
const float3 dP = stack_load_float3(stack, node.fac_offset);
sd->P += dP;
}
}
template<uint node_feature_mask>
ccl_device_noinline void svm_node_displacement(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeDisplacement &node)
{
IF_KERNEL_NODES_FEATURE(BUMP)
{
const float height = stack_load(stack, node.height);
const float midlevel = stack_load(stack, node.midlevel);
const float scale = stack_load(stack, node.scale);
const float3 normal = stack_load_float3_default(stack, node.normal_offset, sd->N);
float3 dP = normal;
if (node.space == NODE_NORMAL_MAP_OBJECT) {
/* Object space. */
object_inverse_normal_transform(kg, sd, &dP);
dP *= (height - midlevel) * scale;
object_dir_transform(kg, sd, &dP);
}
else {
/* World space. */
dP *= (height - midlevel) * scale;
}
stack_store_float3(stack, node.out_offset, dP);
}
else {
stack_store_float3(stack, node.out_offset, zero_float3());
}
}
template<uint node_feature_mask>
ccl_device_noinline void svm_node_vector_displacement(
KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeVectorDisplacement &node)
{
IF_KERNEL_NODES_FEATURE(BUMP)
{
const float3 vector = stack_load(stack, node.vector);
const float midlevel = stack_load(stack, node.midlevel);
const float scale = stack_load(stack, node.scale);
float3 dP = (vector - make_float3(midlevel, midlevel, midlevel)) * scale;
if (node.space == NODE_NORMAL_MAP_TANGENT) {
/* Tangent space. */
float3 normal = sd->N;
object_inverse_normal_transform(kg, sd, &normal);
const AttributeDescriptor attr = find_attribute(kg, sd, node.attr);
float3 tangent;
if (is_attribute_found(attr)) {
tangent = primitive_surface_attribute<float3>(kg, sd, attr);
}
else {
tangent = normalize(sd->dPdu);
}
float3 bitangent = safe_normalize(cross(normal, tangent));
const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.attr_sign);
if (is_attribute_found(attr_sign)) {
const float sign = primitive_surface_attribute<float>(kg, sd, attr_sign);
bitangent *= sign;
}
dP = tangent * dP.x + normal * dP.y + bitangent * dP.z;
}
if (node.space != NODE_NORMAL_MAP_WORLD) {
/* Tangent or object space. */
object_dir_transform(kg, sd, &dP);
}
stack_store_float3(stack, node.displacement_offset, dP);
}
else {
stack_store_float3(stack, node.displacement_offset, zero_float3());
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,550 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/noise.h"
CCL_NAMESPACE_BEGIN
/* Fractal Brownian motion. */
ccl_device_noinline float noise_fbm(
float p, const float detail, const float roughness, const float lacunarity, bool normalize)
{
float fscale = 1.0f;
float amp = 1.0f;
float maxamp = 0.0f;
float sum = 0.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
const float t = snoise_1d(fscale * p);
sum += t * amp;
maxamp += amp;
amp *= roughness;
fscale *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float t = snoise_1d(fscale * p);
const float sum2 = sum + t * amp;
return normalize ? mix(0.5f * sum / maxamp + 0.5f, 0.5f * sum2 / (maxamp + amp) + 0.5f, rmd) :
mix(sum, sum2, rmd);
}
return normalize ? 0.5f * sum / maxamp + 0.5f : sum;
}
ccl_device_noinline float noise_fbm(
float2 p, const float detail, const float roughness, const float lacunarity, bool normalize)
{
float fscale = 1.0f;
float amp = 1.0f;
float maxamp = 0.0f;
float sum = 0.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
const float t = snoise_2d(fscale * p);
sum += t * amp;
maxamp += amp;
amp *= roughness;
fscale *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float t = snoise_2d(fscale * p);
const float sum2 = sum + t * amp;
return normalize ? mix(0.5f * sum / maxamp + 0.5f, 0.5f * sum2 / (maxamp + amp) + 0.5f, rmd) :
mix(sum, sum2, rmd);
}
return normalize ? 0.5f * sum / maxamp + 0.5f : sum;
}
ccl_device_noinline float noise_fbm(
float3 p, const float detail, const float roughness, const float lacunarity, bool normalize)
{
float fscale = 1.0f;
float amp = 1.0f;
float maxamp = 0.0f;
float sum = 0.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
const float t = snoise_3d(fscale * p);
sum += t * amp;
maxamp += amp;
amp *= roughness;
fscale *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float t = snoise_3d(fscale * p);
const float sum2 = sum + t * amp;
return normalize ? mix(0.5f * sum / maxamp + 0.5f, 0.5f * sum2 / (maxamp + amp) + 0.5f, rmd) :
mix(sum, sum2, rmd);
}
return normalize ? 0.5f * sum / maxamp + 0.5f : sum;
}
ccl_device_noinline float noise_fbm(
float4 p, const float detail, const float roughness, const float lacunarity, bool normalize)
{
float fscale = 1.0f;
float amp = 1.0f;
float maxamp = 0.0f;
float sum = 0.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
const float t = snoise_4d(fscale * p);
sum += t * amp;
maxamp += amp;
amp *= roughness;
fscale *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float t = snoise_4d(fscale * p);
const float sum2 = sum + t * amp;
return normalize ? mix(0.5f * sum / maxamp + 0.5f, 0.5f * sum2 / (maxamp + amp) + 0.5f, rmd) :
mix(sum, sum2, rmd);
}
return normalize ? 0.5f * sum / maxamp + 0.5f : sum;
}
/* Multifractal */
ccl_device_noinline float noise_multi_fractal(float p,
const float detail,
const float roughness,
const float lacunarity)
{
float value = 1.0f;
float pwr = 1.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
value *= (pwr * snoise_1d(p) + 1.0f);
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
value *= (rmd * pwr * snoise_1d(p) + 1.0f); /* correct? */
}
return value;
}
ccl_device_noinline float noise_multi_fractal(float2 p,
const float detail,
const float roughness,
const float lacunarity)
{
float value = 1.0f;
float pwr = 1.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
value *= (pwr * snoise_2d(p) + 1.0f);
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
value *= (rmd * pwr * snoise_2d(p) + 1.0f); /* correct? */
}
return value;
}
ccl_device_noinline float noise_multi_fractal(float3 p,
const float detail,
const float roughness,
const float lacunarity)
{
float value = 1.0f;
float pwr = 1.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
value *= (pwr * snoise_3d(p) + 1.0f);
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
value *= (rmd * pwr * snoise_3d(p) + 1.0f); /* correct? */
}
return value;
}
ccl_device_noinline float noise_multi_fractal(float4 p,
const float detail,
const float roughness,
const float lacunarity)
{
float value = 1.0f;
float pwr = 1.0f;
for (int i = 0; i <= float_to_int(detail); i++) {
value *= (pwr * snoise_4d(p) + 1.0f);
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
value *= (rmd * pwr * snoise_4d(p) + 1.0f); /* correct? */
}
return value;
}
/* Heterogeneous Terrain */
ccl_device_noinline float noise_hetero_terrain(
float p, const float detail, const float roughness, const float lacunarity, const float offset)
{
float pwr = roughness;
/* first unscaled octave of function; later octaves are scaled */
float value = offset + snoise_1d(p);
p *= lacunarity;
for (int i = 1; i <= float_to_int(detail); i++) {
const float increment = (snoise_1d(p) + offset) * pwr * value;
value += increment;
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float increment = (snoise_1d(p) + offset) * pwr * value;
value += rmd * increment;
}
return value;
}
ccl_device_noinline float noise_hetero_terrain(float2 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset)
{
float pwr = roughness;
/* first unscaled octave of function; later octaves are scaled */
float value = offset + snoise_2d(p);
p *= lacunarity;
for (int i = 1; i <= float_to_int(detail); i++) {
const float increment = (snoise_2d(p) + offset) * pwr * value;
value += increment;
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float increment = (snoise_2d(p) + offset) * pwr * value;
value += rmd * increment;
}
return value;
}
ccl_device_noinline float noise_hetero_terrain(float3 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset)
{
float pwr = roughness;
/* first unscaled octave of function; later octaves are scaled */
float value = offset + snoise_3d(p);
p *= lacunarity;
for (int i = 1; i <= float_to_int(detail); i++) {
const float increment = (snoise_3d(p) + offset) * pwr * value;
value += increment;
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float increment = (snoise_3d(p) + offset) * pwr * value;
value += rmd * increment;
}
return value;
}
ccl_device_noinline float noise_hetero_terrain(float4 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset)
{
float pwr = roughness;
/* first unscaled octave of function; later octaves are scaled */
float value = offset + snoise_4d(p);
p *= lacunarity;
for (int i = 1; i <= float_to_int(detail); i++) {
const float increment = (snoise_4d(p) + offset) * pwr * value;
value += increment;
pwr *= roughness;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if (rmd != 0.0f) {
const float increment = (snoise_4d(p) + offset) * pwr * value;
value += rmd * increment;
}
return value;
}
/* Hybrid Additive/Multiplicative Multifractal Terrain */
ccl_device_noinline float noise_hybrid_multi_fractal(float p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = 1.0f;
float value = 0.0f;
float weight = 1.0f;
for (int i = 0; (weight > 0.001f) && (i <= float_to_int(detail)); i++) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_1d(p) + offset) * pwr;
pwr *= roughness;
value += weight * signal;
weight *= gain * signal;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if ((rmd != 0.0f) && (weight > 0.001f)) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_1d(p) + offset) * pwr;
value += rmd * weight * signal;
}
return value;
}
ccl_device_noinline float noise_hybrid_multi_fractal(float2 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = 1.0f;
float value = 0.0f;
float weight = 1.0f;
for (int i = 0; (weight > 0.001f) && (i <= float_to_int(detail)); i++) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_2d(p) + offset) * pwr;
pwr *= roughness;
value += weight * signal;
weight *= gain * signal;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if ((rmd != 0.0f) && (weight > 0.001f)) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_2d(p) + offset) * pwr;
value += rmd * weight * signal;
}
return value;
}
ccl_device_noinline float noise_hybrid_multi_fractal(float3 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = 1.0f;
float value = 0.0f;
float weight = 1.0f;
for (int i = 0; (weight > 0.001f) && (i <= float_to_int(detail)); i++) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_3d(p) + offset) * pwr;
pwr *= roughness;
value += weight * signal;
weight *= gain * signal;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if ((rmd != 0.0f) && (weight > 0.001f)) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_3d(p) + offset) * pwr;
value += rmd * weight * signal;
}
return value;
}
ccl_device_noinline float noise_hybrid_multi_fractal(float4 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = 1.0f;
float value = 0.0f;
float weight = 1.0f;
for (int i = 0; (weight > 0.001f) && (i <= float_to_int(detail)); i++) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_4d(p) + offset) * pwr;
pwr *= roughness;
value += weight * signal;
weight *= gain * signal;
p *= lacunarity;
}
const float rmd = detail - floorf(detail);
if ((rmd != 0.0f) && (weight > 0.001f)) {
weight = fminf(weight, 1.0f);
const float signal = (snoise_4d(p) + offset) * pwr;
value += rmd * weight * signal;
}
return value;
}
/* Ridged Multifractal Terrain */
ccl_device_noinline float noise_ridged_multi_fractal(float p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = roughness;
float signal = offset - fabsf(snoise_1d(p));
signal *= signal;
float value = signal;
float weight = 1.0f;
for (int i = 1; i <= float_to_int(detail); i++) {
p *= lacunarity;
weight = saturatef(signal * gain);
signal = offset - fabsf(snoise_1d(p));
signal *= signal;
signal *= weight;
value += signal * pwr;
pwr *= roughness;
}
return value;
}
ccl_device_noinline float noise_ridged_multi_fractal(float2 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = roughness;
float signal = offset - fabsf(snoise_2d(p));
signal *= signal;
float value = signal;
float weight = 1.0f;
for (int i = 1; i <= float_to_int(detail); i++) {
p *= lacunarity;
weight = saturatef(signal * gain);
signal = offset - fabsf(snoise_2d(p));
signal *= signal;
signal *= weight;
value += signal * pwr;
pwr *= roughness;
}
return value;
}
ccl_device_noinline float noise_ridged_multi_fractal(float3 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = roughness;
float signal = offset - fabsf(snoise_3d(p));
signal *= signal;
float value = signal;
float weight = 1.0f;
for (int i = 1; i <= float_to_int(detail); i++) {
p *= lacunarity;
weight = saturatef(signal * gain);
signal = offset - fabsf(snoise_3d(p));
signal *= signal;
signal *= weight;
value += signal * pwr;
pwr *= roughness;
}
return value;
}
ccl_device_noinline float noise_ridged_multi_fractal(float4 p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain)
{
float pwr = roughness;
float signal = offset - fabsf(snoise_4d(p));
signal *= signal;
float value = signal;
float weight = 1.0f;
for (int i = 1; i <= float_to_int(detail); i++) {
p *= lacunarity;
weight = saturatef(signal * gain);
signal = offset - fabsf(snoise_4d(p));
signal *= signal;
signal *= weight;
value += signal * pwr;
pwr *= roughness;
}
return value;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/closure/bsdf_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Fresnel Node */
ccl_device_noinline void svm_node_fresnel(ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeFresnel &ccl_restrict node)
{
float eta = stack_load(stack, node.ior);
const float3 normal_in = stack_load_float3_default(stack, node.normal_offset, sd->N);
eta = fmaxf(eta, 1e-5f);
eta = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta;
const float f = fresnel_dielectric_cos(dot(sd->wi, normal_in), eta);
stack_store_float(stack, node.out_offset, f);
}
/* Layer Weight Node */
ccl_device_noinline void svm_node_layer_weight(ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeLayerWeight &ccl_restrict
node)
{
float blend = stack_load(stack, node.blend);
const float3 normal_in = stack_load_float3_default(stack, node.normal_offset, sd->N);
float f;
if (node.weight_type == NODE_LAYER_WEIGHT_FRESNEL) {
float eta = fmaxf(1.0f - blend, 1e-5f);
eta = (sd->flag & SD_BACKFACING) ? eta : 1.0f / eta;
f = fresnel_dielectric_cos(dot(sd->wi, normal_in), eta);
}
else {
f = fabsf(dot(sd->wi, normal_in));
if (blend != 0.5f) {
blend = clamp(blend, 0.0f, 1.0f - 1e-5f);
blend = (blend < 0.5f) ? 2.0f * blend : 0.5f / (1.0f - blend);
f = powf(f, blend);
}
f = 1.0f - f;
}
stack_store_float(stack, node.out_offset, f);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,362 @@
/* SPDX-FileCopyrightText: 2024 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Implements Gabor noise based on the paper:
*
* Lagae, Ares, et al. "Procedural noise using sparse Gabor convolution." ACM Transactions on
* Graphics (TOG) 28.3 (2009): 1-10.
*
* But with the improvements from the paper:
*
* Tavernier, Vincent, et al. "Making gabor noise fast and normalized." Eurographics 2019-40th
* Annual Conference of the European Association for Computer Graphics. 2019.
*
* And compute the Phase and Intensity of the Gabor based on the paper:
*
* Tricard, Thibault, et al. "Procedural phasor noise." ACM Transactions on Graphics (TOG) 38.4
* (2019): 1-13.
*/
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "util/hash.h"
CCL_NAMESPACE_BEGIN
/* The original Gabor noise paper specifies that the impulses count for each cell should be
* computed by sampling a Poisson distribution whose mean is the impulse density. However,
* Tavernier's paper showed that stratified Poisson point sampling is better assuming the weights
* are sampled using a Bernoulli distribution, as shown in Figure (3). By stratified sampling, they
* mean a constant number of impulses per cell, so the stratification is the grid itself in that
* sense, as described in the supplementary material of the paper. */
#define IMPULSES_COUNT 8 // NOLINT
/* Computes a 2D Gabor kernel based on Equation (6) in the original Gabor noise paper. Where the
* frequency argument is the F_0 parameter and the orientation argument is the w_0 parameter. We
* assume the Gaussian envelope has a unit magnitude, that is, K = 1. That is because we will
* eventually normalize the final noise value to the unit range, so the multiplication by the
* magnitude will be canceled by the normalization. Further, we also assume a unit Gaussian width,
* that is, a = 1. That is because it does not provide much artistic control. It follows that the
* Gaussian will be truncated at pi.
*
* To avoid the discontinuities caused by the aforementioned truncation, the Gaussian is windowed
* using a Hann window, that is because contrary to the claim made in the original Gabor paper,
* truncating the Gaussian produces significant artifacts especially when differentiated for bump
* mapping. The Hann window is C1 continuous and has limited effect on the shape of the Gaussian,
* so it felt like an appropriate choice.
*
* Finally, instead of computing the Gabor value directly, we instead use the complex phasor
* formulation described in section 3.1.1 in Tricard's paper. That's done to be able to compute the
* phase and intensity of the Gabor noise after summation based on equations (8) and (9). The
* return value of the Gabor kernel function is then a complex number whose real value is the
* value computed in the original Gabor noise paper, and whose imaginary part is the sine
* counterpart of the real part, which is the only extra computation in the new formulation.
*
* Note that while the original Gabor noise paper uses the cosine part of the phasor, that is, the
* real part of the phasor, we use the sine part instead, that is, the imaginary part of the
* phasor, as suggested by Tavernier's paper in "Section 3.3. Instance stationarity and
* normalization", to ensure a zero mean, which should help with normalization. */
ccl_device float2 compute_2d_gabor_kernel(const float2 position,
const float frequency,
const float orientation)
{
const float distance_squared = dot(position, position);
const float hann_window = 0.5f + 0.5f * cosf(M_PI_F * distance_squared);
const float gaussian_envelop = expf(-M_PI_F * distance_squared);
const float windowed_gaussian_envelope = gaussian_envelop * hann_window;
const float angle = 2.0f * M_PI_F * dot(position, polar_to_cartesian(frequency, orientation));
return polar_to_cartesian(windowed_gaussian_envelope, angle);
}
/**
* Computes the approximate standard deviation of the zero mean normal distribution representing
* the amplitude distribution of the noise based on Equation (9) in the original Gabor noise paper.
* For simplicity, the Hann window is ignored and the orientation is fixed since the variance is
* orientation invariant. We start integrating the squared Gabor kernel with respect to x:
*
* \code{.tex}
* \int_{-\infty}^{-\infty} (e^{- \pi (x^2 + y^2)} cos(2 \pi f_0 x))^2 dx
* \endcode
*
* Which gives:
*
* \code{.tex}
* \frac{(e^{2 \pi f_0^2}-1) e^{-2 \pi y^2 - 2 pi f_0^2}}{2^\frac{3}{2}}
* \endcode
*
* Then we similarly integrate with respect to y to get:
*
* \code{.tex}
* \frac{1 - e^{-2 \pi f_0^2}}{4}
* \endcode
*
* Secondly, we note that the second moment of the weights distribution is 0.5 since it is a
* fair Bernoulli distribution. So the final standard deviation expression is square root the
* integral multiplied by the impulse density multiplied by the second moment.
*
* Note however that the integral is almost constant for all frequencies larger than one, and
* converges to an upper limit as the frequency approaches infinity, so we replace the expression
* with the following limit:
*
* \code{.tex}
* \lim_{x \to \infty} \frac{1 - e^{-2 \pi f_0^2}}{4}
* \endcode
*
* To get an approximation of 0.25.
*/
ccl_device float compute_2d_gabor_standard_deviation()
{
const float integral_of_gabor_squared = 0.25f;
const float second_moment = 0.5f;
return sqrtf(IMPULSES_COUNT * second_moment * integral_of_gabor_squared);
}
/* Computes the Gabor noise value at the given position for the given cell. This is essentially the
* sum in Equation (8) in the original Gabor noise paper, where we sum Gabor kernels sampled at a
* random position with a random weight. The orientation of the kernel is constant for anisotropic
* noise while it is random for isotropic noise. The original Gabor noise paper mentions that the
* weights should be uniformly distributed in the [-1, 1] range, however, Tavernier's paper showed
* that using a Bernoulli distribution yields better results, so that is what we do. */
ccl_device float2 compute_2d_gabor_noise_cell(float2 cell,
const float2 position,
const float frequency,
const float isotropy,
const float base_orientation)
{
float2 noise = make_float2(0.0f, 0.0f);
for (int i = 0; i < IMPULSES_COUNT; ++i) {
/* Compute unique seeds for each of the needed random variables. */
const float3 seed_for_orientation = make_float3(cell.x, cell.y, i * 3);
const float3 seed_for_kernel_center = make_float3(cell.x, cell.y, i * 3 + 1);
const float3 seed_for_weight = make_float3(cell.x, cell.y, i * 3 + 2);
/* For isotropic noise, add a random orientation amount, while for anisotropic noise, use the
* base orientation. Linearly interpolate between the two cases using the isotropy factor. Note
* that the random orientation range spans pi as opposed to two pi, that's because the Gabor
* kernel is symmetric around pi. */
const float random_orientation = (hash_float3_to_float(seed_for_orientation) - 0.5f) * M_PI_F;
const float orientation = base_orientation + random_orientation * isotropy;
const float2 kernel_center = hash_float3_to_float2(seed_for_kernel_center);
const float2 position_in_kernel_space = position - kernel_center;
/* The kernel is windowed beyond the unit distance, so early exit with a zero for points that
* are further than a unit radius. */
if (dot(position_in_kernel_space, position_in_kernel_space) >= 1.0f) {
continue;
}
/* We either add or subtract the Gabor kernel based on a Bernoulli distribution of equal
* probability. */
const float weight = hash_float3_to_float(seed_for_weight) < 0.5f ? -1.0f : 1.0f;
noise += weight * compute_2d_gabor_kernel(position_in_kernel_space, frequency, orientation);
}
return noise;
}
/* Computes the Gabor noise value by dividing the space into a grid and evaluating the Gabor noise
* in the space of each cell of the 3x3 cell neighborhood. */
ccl_device float2 compute_2d_gabor_noise(const float2 coordinates,
const float frequency,
const float isotropy,
const float base_orientation)
{
const float2 cell_position = floor(coordinates);
const float2 local_position = coordinates - cell_position;
float2 sum = make_float2(0.0f, 0.0f);
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
const float2 cell_offset = make_float2(i, j);
const float2 current_cell_position = cell_position + cell_offset;
const float2 position_in_cell_space = local_position - cell_offset;
sum += compute_2d_gabor_noise_cell(
current_cell_position, position_in_cell_space, frequency, isotropy, base_orientation);
}
}
return sum;
}
/* Identical to compute_2d_gabor_kernel, except it is evaluated in 3D space. Notice that Equation
* (6) in the original Gabor noise paper computes the frequency vector using (cos(w_0), sin(w_0)),
* which we also do in the 2D variant, however, for 3D, the orientation is already a unit frequency
* vector, so we just need to scale it by the frequency value. */
ccl_device float2 compute_3d_gabor_kernel(const float3 position,
const float frequency,
const float3 orientation)
{
const float distance_squared = dot(position, position);
const float hann_window = 0.5f + 0.5f * cosf(M_PI_F * distance_squared);
const float gaussian_envelop = expf(-M_PI_F * distance_squared);
const float windowed_gaussian_envelope = gaussian_envelop * hann_window;
const float3 frequency_vector = frequency * orientation;
const float angle = 2.0f * M_PI_F * dot(position, frequency_vector);
return polar_to_cartesian(windowed_gaussian_envelope, angle);
}
/* Identical to compute_2d_gabor_standard_deviation except we do triple integration in 3D. The only
* difference is the denominator in the integral expression, which is `2^{5 / 2}` for the 3D case
* instead of 4 for the 2D case. Similarly, the limit evaluates to `1 / (4 * sqrt(2))`. */
ccl_device float compute_3d_gabor_standard_deviation()
{
const float integral_of_gabor_squared = 1.0f / (4.0f * M_SQRT2_F);
const float second_moment = 0.5f;
return sqrtf(IMPULSES_COUNT * second_moment * integral_of_gabor_squared);
}
/* Computes the orientation of the Gabor kernel such that it is constant for anisotropic
* noise while it is random for isotropic noise. We randomize in spherical coordinates for a
* uniform distribution. */
ccl_device float3 compute_3d_orientation(const float3 orientation,
const float isotropy,
const float4 seed)
{
/* Return the base orientation in case we are completely anisotropic. */
if (isotropy == 0.0f) {
return orientation;
}
/* Compute the orientation in spherical coordinates. */
float inclination = acosf(orientation.z);
float azimuth = (orientation.y < 0.0f ? -1.0f : 1.0f) *
acosf(orientation.x / len(make_float2(orientation.x, orientation.y)));
/* For isotropic noise, add a random orientation amount, while for anisotropic noise, use the
* base orientation. Linearly interpolate between the two cases using the isotropy factor. Note
* that the random orientation range is to pi as opposed to two pi, that's because the Gabor
* kernel is symmetric around pi. */
const float2 random_angles = hash_float4_to_float2(seed) * M_PI_F;
inclination += random_angles.x * isotropy;
azimuth += random_angles.y * isotropy;
/* Convert back to Cartesian coordinates. */
return spherical_to_direction(inclination, azimuth);
}
ccl_device float2 compute_3d_gabor_noise_cell(float3 cell,
const float3 position,
const float frequency,
const float isotropy,
const float3 base_orientation)
{
float2 noise = make_float2(0.0f, 0.0f);
for (int i = 0; i < IMPULSES_COUNT; ++i) {
/* Compute unique seeds for each of the needed random variables. */
const float4 seed_for_orientation = make_float4(cell, i * 3);
const float4 seed_for_kernel_center = make_float4(cell, i * 3 + 1);
const float4 seed_for_weight = make_float4(cell, i * 3 + 2);
const float3 orientation = compute_3d_orientation(
base_orientation, isotropy, seed_for_orientation);
const float3 kernel_center = hash_float4_to_float3(seed_for_kernel_center);
const float3 position_in_kernel_space = position - kernel_center;
/* The kernel is windowed beyond the unit distance, so early exit with a zero for points that
* are further than a unit radius. */
if (dot(position_in_kernel_space, position_in_kernel_space) >= 1.0f) {
continue;
}
/* We either add or subtract the Gabor kernel based on a Bernoulli distribution of equal
* probability. */
const float weight = hash_float4_to_float(seed_for_weight) < 0.5f ? -1.0f : 1.0f;
noise += weight * compute_3d_gabor_kernel(position_in_kernel_space, frequency, orientation);
}
return noise;
}
/* Identical to compute_2d_gabor_noise but works in the 3D neighborhood of the noise. */
ccl_device float2 compute_3d_gabor_noise(const float3 coordinates,
const float frequency,
const float isotropy,
const float3 base_orientation)
{
const float3 cell_position = floor(coordinates);
const float3 local_position = coordinates - cell_position;
float2 sum = make_float2(0.0f, 0.0f);
for (int k = -1; k <= 1; k++) {
for (int j = -1; j <= 1; j++) {
for (int i = -1; i <= 1; i++) {
const float3 cell_offset = make_float3(i, j, k);
const float3 current_cell_position = cell_position + cell_offset;
const float3 position_in_cell_space = local_position - cell_offset;
sum += compute_3d_gabor_noise_cell(
current_cell_position, position_in_cell_space, frequency, isotropy, base_orientation);
}
}
}
return sum;
}
ccl_device_noinline void svm_node_tex_gabor(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexGabor &ccl_restrict node)
{
const float3 coordinates = stack_load_float3(stack, node.coordinates);
const float scale = stack_load(stack, node.scale);
float frequency = stack_load(stack, node.frequency);
const float anisotropy = stack_load(stack, node.anisotropy);
const float orientation_2d = stack_load(stack, node.orientation_2d);
const float3 orientation_3d = stack_load(stack, node.orientation_3d);
const float3 scaled_coordinates = coordinates * scale;
const float isotropy = 1.0f - clamp(anisotropy, 0.0f, 1.0f);
frequency = max(0.001f, frequency);
float2 phasor = make_float2(0.0f, 0.0f);
float standard_deviation = 1.0f;
switch (node.gabor_type) {
case NODE_GABOR_TYPE_2D: {
phasor = compute_2d_gabor_noise(make_float2(scaled_coordinates.x, scaled_coordinates.y),
frequency,
isotropy,
orientation_2d);
standard_deviation = compute_2d_gabor_standard_deviation();
break;
}
case NODE_GABOR_TYPE_3D: {
const float3 orientation = normalize(orientation_3d);
phasor = compute_3d_gabor_noise(scaled_coordinates, frequency, isotropy, orientation);
standard_deviation = compute_3d_gabor_standard_deviation();
break;
}
}
/* Normalize the noise by dividing by six times the standard deviation, which was determined
* empirically. */
const float normalization_factor = 6.0f * standard_deviation;
/* As discussed in compute_2d_gabor_kernel, we use the imaginary part of the phasor as the Gabor
* value. But remap to [0, 1] from [-1, 1]. */
if (stack_valid(node.value_offset)) {
stack_store_float(stack, node.value_offset, (phasor.y / normalization_factor) * 0.5f + 0.5f);
}
/* Compute the phase based on equation (9) in Tricard's paper. But remap the phase into the
* [0, 1] range. */
if (stack_valid(node.phase_offset)) {
const float phase = (atan2f(phasor.y, phasor.x) + M_PI_F) / (2.0f * M_PI_F);
stack_store_float(stack, node.phase_offset, phase);
}
/* Compute the intensity based on equation (8) in Tricard's paper. */
if (stack_valid(node.intensity_offset)) {
stack_store_float(stack, node.intensity_offset, len(phasor) / normalization_factor);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/math_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_gamma(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeGamma &ccl_restrict node)
{
float3 color = stack_load(stack, node.color);
const float gamma = stack_load(stack, node.gamma);
color = svm_math_gamma_color(color, gamma);
if (stack_valid(node.out_offset)) {
stack_store_float3(stack, node.out_offset, color);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,248 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/geom/curve.h"
#include "kernel/geom/primitive.h"
#include "kernel/svm/attribute.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "util/hash.h"
CCL_NAMESPACE_BEGIN
/* Geometry Node */
template<typename Float3Type>
ccl_device_inline Float3Type svm_node_geometry_eval(KernelGlobals kg,
ccl_private ShaderData *sd,
const NodeGeometry type)
{
Float3Type data;
switch (type) {
case NODE_GEOM_P:
data = shading_position<Float3Type>(sd);
break;
case NODE_GEOM_N:
data = Float3Type(sd->N);
break;
#ifdef __DPDU__
case NODE_GEOM_T:
data = primitive_tangent<Float3Type>(kg, sd);
break;
#endif
case NODE_GEOM_I:
data = shading_incoming<Float3Type>(sd);
break;
case NODE_GEOM_Ng:
data = Float3Type(sd->Ng);
break;
case NODE_GEOM_uv:
data = Float3Type(make_float3(1.0f - sd->u - sd->v, sd->u, 0.0f));
if constexpr (is_dual_v<Float3Type>) {
data.dx = make_float3(-sd->du.dx - sd->dv.dx, sd->du.dx, 0.0f);
data.dy = make_float3(-sd->du.dy - sd->dv.dy, sd->du.dy, 0.0f);
}
break;
default:
data = Float3Type(make_float3(0.0f, 0.0f, 0.0f));
}
return data;
}
template<typename Float3Type>
ccl_device_noinline void svm_node_geometry(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeGeometry &ccl_restrict node)
{
Float3Type data = svm_node_geometry_eval<Float3Type>(kg, sd, node.geom_type);
if constexpr (is_dual_v<Float3Type>) {
/* Apply first-order bump offset. */
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) {
stack_store(stack, node.out_offset, data);
}
else {
stack_store(stack, node.out_offset, data.val);
}
}
else {
stack_store(stack, node.out_offset, data);
}
}
/* Object Info */
ccl_device_noinline void svm_node_object_info(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeObjectInfo &ccl_restrict
node)
{
float data;
switch (node.info_type) {
case NODE_INFO_OB_LOCATION: {
stack_store_float3(stack, node.out_offset, object_location(kg, sd));
return;
}
case NODE_INFO_OB_COLOR: {
stack_store_float3(stack, node.out_offset, object_color(kg, sd->object));
return;
}
case NODE_INFO_OB_ALPHA:
data = object_alpha(kg, sd->object);
break;
case NODE_INFO_OB_INDEX:
data = object_pass_id(kg, sd->object);
break;
case NODE_INFO_MAT_INDEX:
data = shader_pass_id(kg, sd);
break;
case NODE_INFO_OB_RANDOM: {
data = object_random_number(kg, sd->object);
break;
}
default:
data = 0.0f;
break;
}
stack_store_float(stack, node.out_offset, data);
}
/* Particle Info */
ccl_device_noinline void svm_node_particle_info(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeParticleInfo &ccl_restrict
node)
{
switch (node.info_type) {
case NODE_INFO_PAR_INDEX: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float(stack, node.out_offset, particle_index(kg, particle_id));
break;
}
case NODE_INFO_PAR_RANDOM: {
const int particle_id = object_particle_id(kg, sd->object);
const float random = hash_uint2_to_float(particle_index(kg, particle_id), 0);
stack_store_float(stack, node.out_offset, random);
break;
}
case NODE_INFO_PAR_AGE: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float(stack, node.out_offset, particle_age(kg, particle_id));
break;
}
case NODE_INFO_PAR_LIFETIME: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float(stack, node.out_offset, particle_lifetime(kg, particle_id));
break;
}
case NODE_INFO_PAR_LOCATION: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float3(stack, node.out_offset, particle_location(kg, particle_id));
break;
}
#if 0 /* XXX float4 currently not supported in SVM stack */
case NODE_INFO_PAR_ROTATION: {
int particle_id = object_particle_id(kg, sd->object);
stack_store_float4(stack, node.out_offset, particle_rotation(kg, particle_id));
break;
}
#endif
case NODE_INFO_PAR_SIZE: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float(stack, node.out_offset, particle_size(kg, particle_id));
break;
}
case NODE_INFO_PAR_VELOCITY: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float3(stack, node.out_offset, particle_velocity(kg, particle_id));
break;
}
case NODE_INFO_PAR_ANGULAR_VELOCITY: {
const int particle_id = object_particle_id(kg, sd->object);
stack_store_float3(stack, node.out_offset, particle_angular_velocity(kg, particle_id));
break;
}
}
}
#ifdef __HAIR__
/* Hair Info */
ccl_device_noinline void svm_node_hair_info(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeHairInfo &ccl_restrict node)
{
float data;
float3 data3;
switch (node.info_type) {
case NODE_INFO_CURVE_IS_STRAND: {
data = (sd->type & PRIMITIVE_CURVE) != 0;
stack_store_float(stack, node.out_offset, data);
break;
}
case NODE_INFO_CURVE_INTERCEPT:
break; /* handled as attribute */
case NODE_INFO_CURVE_LENGTH:
break; /* handled as attribute */
case NODE_INFO_CURVE_RANDOM:
break; /* handled as attribute */
case NODE_INFO_CURVE_THICKNESS: {
data = curve_thickness(kg, sd);
stack_store_float(stack, node.out_offset, data);
break;
}
case NODE_INFO_CURVE_TANGENT_NORMAL: {
data3 = curve_tangent_normal(sd);
stack_store_float3(stack, node.out_offset, data3);
break;
}
}
}
#endif
#ifdef __POINTCLOUD__
/* Point Info */
ccl_device_noinline void svm_node_point_info(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodePointInfo &ccl_restrict node)
{
switch (node.info_type) {
case NODE_INFO_POINT_POSITION:
stack_store_float3(stack, node.out_offset, point_position(kg, sd));
break;
case NODE_INFO_POINT_RADIUS:
stack_store_float(stack, node.out_offset, point_radius(kg, sd));
break;
case NODE_INFO_POINT_RANDOM:
break; /* handled as attribute */
}
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,75 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Gradient */
ccl_device float svm_gradient(const float3 p, NodeGradientType type)
{
float x;
float y;
float z;
x = p.x;
y = p.y;
z = p.z;
if (type == NODE_BLEND_LINEAR) {
return x;
}
if (type == NODE_BLEND_QUADRATIC) {
const float r = fmaxf(x, 0.0f);
return r * r;
}
if (type == NODE_BLEND_EASING) {
const float r = fminf(fmaxf(x, 0.0f), 1.0f);
const float t = r * r;
return (3.0f * t - 2.0f * t * r);
}
if (type == NODE_BLEND_DIAGONAL) {
return (x + y) * 0.5f;
}
if (type == NODE_BLEND_RADIAL) {
return atan2f(y, x) / M_2PI_F + 0.5f;
}
/* Bias a little bit for the case where p is a unit length vector,
* to get exactly zero instead of a small random value depending
* on float precision. */
const float r = fmaxf(0.999999f - sqrtf(x * x + y * y + z * z), 0.0f);
if (type == NODE_BLEND_QUADRATIC_SPHERE) {
return r * r;
}
if (type == NODE_BLEND_SPHERICAL) {
return r;
}
return 0.0f;
}
ccl_device_noinline void svm_node_tex_gradient(
ccl_private float *ccl_restrict stack, const ccl_global SVMNodeTexGradient &ccl_restrict node)
{
const float3 co = stack_load_float3(stack, node.co);
float f = svm_gradient(co, node.gradient_type);
f = saturatef(f);
if (stack_valid(node.fac_offset)) {
stack_store_float(stack, node.fac_offset, f);
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, make_float3(f, f, f));
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "util/color.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_hsv(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeHSV &ccl_restrict node)
{
const float fac = stack_load(stack, node.fac);
const float3 in_color = stack_load(stack, node.color);
float3 color = in_color;
const float hue = stack_load(stack, node.hue);
const float sat = stack_load(stack, node.sat);
const float val = stack_load(stack, node.val);
color = rgb_to_hsv(color);
color.x = fractf(color.x + hue + 0.5f);
color.y = saturatef(color.y * sat);
color.z *= val;
color = hsv_to_rgb(color);
color.x = fac * color.x + (1.0f - fac) * in_color.x;
color.y = fac * color.y + (1.0f - fac) * in_color.y;
color.z = fac * color.z + (1.0f - fac) * in_color.z;
/* Clamp color to prevent negative values caused by over saturation. */
color.x = max(color.x, 0.0f);
color.y = max(color.y, 0.0f);
color.z = max(color.z, 0.0f);
if (stack_valid(node.out_color_offset)) {
stack_store_float3(stack, node.out_color_offset, color);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,33 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/ies.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_ies(KernelGlobals kg,
ccl_private ShaderData * /*sd*/,
ccl_private float *stack,
const ccl_global SVMNodeIES &ccl_restrict node)
{
float3 vector = stack_load_float3(stack, node.vector_offset);
const float strength = stack_load(stack, node.strength);
vector = normalize(vector);
const float v_angle = safe_acosf(-vector.z);
const float h_angle = atan2f(vector.x, vector.y) + M_PI_F;
const float fac = strength * kernel_ies_interp(kg, node.slot, h_angle, v_angle);
if (stack_valid(node.fac_offset)) {
stack_store_float(stack, node.fac_offset, fac);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,207 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/image.h"
#include "kernel/camera/projection.h"
#include "kernel/geom/object.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "util/color.h"
#include "util/types_image.h"
CCL_NAMESPACE_BEGIN
ccl_device float4 svm_image_texture(
KernelGlobals kg, ccl_private ShaderData *sd, const int id, const dual2 uv, const uint flags)
{
float4 r = kernel_image_interp_with_udim(kg, sd, id, uv);
const float alpha = r.w;
if ((flags & NODE_IMAGE_ALPHA_UNASSOCIATE) && alpha != 1.0f && alpha != 0.0f) {
r /= alpha;
r.w = alpha;
}
if (flags & NODE_IMAGE_COMPRESS_AS_SRGB) {
r = color_srgb_to_linear_v4(r);
}
return r;
}
/* Remap coordinate from 0..1 box to -1..-1 */
template<class Float3Type> ccl_device_inline Float3Type texco_remap_square(const Float3Type co)
{
return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f;
}
template<class Float3Type>
ccl_device_inline auto svm_node_tex_image_mapping(const Float3Type co, const uint proj)
{
if (proj == NODE_IMAGE_PROJ_SPHERE) {
return map_to_sphere(texco_remap_square(co));
}
if (proj == NODE_IMAGE_PROJ_TUBE) {
return map_to_tube(texco_remap_square(co));
}
return make_float2(co);
}
template<class Float3Type>
ccl_device_noinline void svm_node_tex_image(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexImage &ccl_restrict node)
{
const Float3Type co = stack_load<Float3Type>(stack, node.co);
const dual2 tex_co(svm_node_tex_image_mapping(co, node.projection));
const float4 f = svm_image_texture(kg, sd, node.id, tex_co, node.flags);
if (stack_valid(node.out_offset)) {
stack_store_float3(stack, node.out_offset, make_float3(f));
}
if (stack_valid(node.alpha_offset)) {
stack_store_float(stack, node.alpha_offset, f.w);
}
}
template<class Float3Type>
ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexImageBox &ccl_restrict
node)
{
/* get object space normal */
float3 N = sd->N;
object_inverse_normal_transform(kg, sd, &N);
/* project from direction vector to barycentric coordinates in triangles */
const float3 signed_N = N;
N = fabs(N);
N /= (N.x + N.y + N.z);
/* basic idea is to think of this as a triangle, each corner representing
* one of the 3 faces of the cube. in the corners we have single textures,
* in between we blend between two textures, and in the middle we a blend
* between three textures.
*
* The `Nxyz` values are the barycentric coordinates in an equilateral
* triangle, which in case of blending, in the middle has a smaller
* equilateral triangle where 3 textures blend. this divides things into
* 7 zones, with an `if()` test for each zone. */
float3 weight = make_float3(0.0f, 0.0f, 0.0f);
const float blend = node.blend;
const float limit = 0.5f * (1.0f + blend);
/* first test for corners with single texture */
if (N.x > limit * (N.x + N.y) && N.x > limit * (N.x + N.z)) {
weight.x = 1.0f;
}
else if (N.y > limit * (N.x + N.y) && N.y > limit * (N.y + N.z)) {
weight.y = 1.0f;
}
else if (N.z > limit * (N.x + N.z) && N.z > limit * (N.y + N.z)) {
weight.z = 1.0f;
}
else if (blend > 0.0f) {
/* in case of blending, test for mixes between two textures */
if (N.z < (1.0f - limit) * (N.y + N.x)) {
weight.x = N.x / (N.x + N.y);
weight.x = saturatef((weight.x - 0.5f * (1.0f - blend)) / blend);
weight.y = 1.0f - weight.x;
}
else if (N.x < (1.0f - limit) * (N.y + N.z)) {
weight.y = N.y / (N.y + N.z);
weight.y = saturatef((weight.y - 0.5f * (1.0f - blend)) / blend);
weight.z = 1.0f - weight.y;
}
else if (N.y < (1.0f - limit) * (N.x + N.z)) {
weight.x = N.x / (N.x + N.z);
weight.x = saturatef((weight.x - 0.5f * (1.0f - blend)) / blend);
weight.z = 1.0f - weight.x;
}
else {
/* last case, we have a mix between three */
weight.x = ((2.0f - limit) * N.x + (limit - 1.0f)) / (2.0f * limit - 1.0f);
weight.y = ((2.0f - limit) * N.y + (limit - 1.0f)) / (2.0f * limit - 1.0f);
weight.z = ((2.0f - limit) * N.z + (limit - 1.0f)) / (2.0f * limit - 1.0f);
}
}
else {
/* Desperate mode, no valid choice anyway, fall back to one side. */
weight.x = 1.0f;
}
/* now fetch textures */
float4 f = zero_float4();
const dual3 co = dual3(stack_load<Float3Type>(stack, node.co));
/* Map so that no textures are flipped, rotation is somewhat arbitrary. */
if (weight.x > 0.0f) {
const dual2 uv = make_float2((signed_N.x < 0.0f) ? 1.0f - co.y() : co.y(), co.z());
f += weight.x * svm_image_texture(kg, sd, node.id, uv, node.flags);
}
if (weight.y > 0.0f) {
const dual2 uv = make_float2((signed_N.y > 0.0f) ? 1.0f - co.x() : co.x(), co.z());
f += weight.y * svm_image_texture(kg, sd, node.id, uv, node.flags);
}
if (weight.z > 0.0f) {
const dual2 uv = make_float2((signed_N.z > 0.0f) ? 1.0f - co.y() : co.y(), co.x());
f += weight.z * svm_image_texture(kg, sd, node.id, uv, node.flags);
}
if (stack_valid(node.out_offset)) {
stack_store_float3(stack, node.out_offset, make_float3(f.x, f.y, f.z));
}
if (stack_valid(node.alpha_offset)) {
stack_store_float(stack, node.alpha_offset, f.w);
}
}
template<class Float3Type>
ccl_device_inline auto svm_node_tex_environment_projection(Float3Type co, const uint proj)
{
co = safe_normalize(co);
if (proj == 0) {
return direction_to_equirectangular(co);
}
return direction_to_mirrorball(co);
}
template<class Float3Type>
ccl_device_noinline void svm_node_tex_environment(
KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexEnvironment &ccl_restrict node)
{
const Float3Type co = stack_load<Float3Type>(stack, node.co);
const dual2 uv(svm_node_tex_environment_projection(co, node.projection));
const float4 f = svm_image_texture(kg, sd, node.id, uv, node.flags);
if (stack_valid(node.out_offset)) {
stack_store_float3(stack, node.out_offset, make_float3(f.x, f.y, f.z));
}
if (stack_valid(node.alpha_offset)) {
stack_store_float(stack, node.alpha_offset, f.w);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,32 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device float invert(const float color, const float factor)
{
return factor * (1.0f - color) + (1.0f - factor) * color;
}
ccl_device_noinline void svm_node_invert(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeInvert &ccl_restrict node)
{
const float factor = stack_load(stack, node.fac);
float3 color = stack_load(stack, node.color);
color.x = invert(color.x, factor);
color.y = invert(color.y, factor);
color.z = invert(color.z, factor);
if (stack_valid(node.out_offset)) {
stack_store_float3(stack, node.out_offset, color);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,144 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Light Path Node */
template<uint node_feature_mask, typename ConstIntegratorGenericState>
ccl_device_noinline void svm_node_light_path(KernelGlobals kg,
ConstIntegratorGenericState state,
const ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeLightPath &ccl_restrict node,
const PathRayVisibility path_visibility,
const uint32_t path_flag)
{
float info = 0.0f;
switch (node.path_type) {
case NODE_LP_camera:
info = (path_visibility & PATH_RAY_VISIBILITY_CAMERA) ? 1.0f : 0.0f;
break;
case NODE_LP_shadow:
info = (path_visibility & PATH_RAY_VISIBILITY_SHADOW) ? 1.0f : 0.0f;
break;
case NODE_LP_diffuse:
info = (path_visibility & PATH_RAY_VISIBILITY_DIFFUSE) ? 1.0f : 0.0f;
break;
case NODE_LP_glossy:
info = (path_visibility & PATH_RAY_VISIBILITY_GLOSSY) ? 1.0f : 0.0f;
break;
case NODE_LP_singular:
info = (path_flag & PATH_RAY_SINGULAR) ? 1.0f : 0.0f;
break;
case NODE_LP_reflection:
info = (path_flag & PATH_RAY_REFLECT) ? 1.0f : 0.0f;
break;
case NODE_LP_transmission:
info = (path_visibility & PATH_RAY_VISIBILITY_TRANSMIT) ? 1.0f : 0.0f;
break;
case NODE_LP_volume_scatter:
info = (path_visibility & PATH_RAY_VISIBILITY_VOLUME_SCATTER) ? 1.0f : 0.0f;
break;
case NODE_LP_backfacing:
info = (sd->flag & SD_BACKFACING) ? 1.0f : 0.0f;
break;
case NODE_LP_ray_length:
info = sd->ray_length;
break;
case NODE_LP_ray_depth: {
/* Read bounce from different locations depending on if this is a shadow
* path. It's a bit dubious to have integrate state details leak into
* this function but hard to avoid currently. */
IF_KERNEL_NODES_FEATURE(LIGHT_PATH)
{
info = (float)integrator_state_bounce(state, path_flag);
}
/* For background, light emission and shadow evaluation from a
* surface or volume we are effectively one bounce further. */
if ((path_visibility & PATH_RAY_VISIBILITY_SHADOW) || (path_flag & PATH_RAY_EMISSION)) {
info += 1.0f;
}
break;
}
case NODE_LP_ray_transparent: {
IF_KERNEL_NODES_FEATURE(LIGHT_PATH)
{
info = (float)integrator_state_transparent_bounce(state, path_flag);
}
break;
}
case NODE_LP_ray_diffuse:
IF_KERNEL_NODES_FEATURE(LIGHT_PATH)
{
info = (float)integrator_state_diffuse_bounce(state, path_flag);
}
break;
case NODE_LP_ray_glossy:
IF_KERNEL_NODES_FEATURE(LIGHT_PATH)
{
info = (float)integrator_state_glossy_bounce(state, path_flag);
}
break;
case NODE_LP_ray_transmission:
IF_KERNEL_NODES_FEATURE(LIGHT_PATH)
{
info = (float)integrator_state_transmission_bounce(state, path_flag);
}
break;
case NODE_LP_ray_portal:
IF_KERNEL_NODES_FEATURE(LIGHT_PATH)
{
info = (float)integrator_state_portal_bounce(kg, state, path_flag);
}
break;
}
stack_store_float(stack, node.out_offset, info);
}
/* Light Falloff Node */
ccl_device_noinline void svm_node_light_falloff(ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeLightFalloff &ccl_restrict
node)
{
float strength = stack_load(stack, node.strength);
if (sd->ray_length == FLT_MAX) {
/* Distant lights (which have a ray_length of FLT_MAX) overflow when using most outputs of
* the light falloff node. So just ignore the node in that case. */
stack_store_float(stack, node.out_offset, strength);
return;
}
switch (node.falloff_type) {
case NODE_LIGHT_FALLOFF_QUADRATIC:
break;
case NODE_LIGHT_FALLOFF_LINEAR:
strength *= sd->ray_length;
break;
case NODE_LIGHT_FALLOFF_CONSTANT:
strength *= sd->ray_length * sd->ray_length;
break;
}
const float smooth = stack_load(stack, node.smooth);
if (smooth > 0.0f) {
const float squared = sd->ray_length * sd->ray_length;
strength *= squared / (smooth + squared);
}
stack_store_float(stack, node.out_offset, strength);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,119 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Magic */
ccl_device_noinline_cpu float3 svm_magic(const float3 p,
const float scale,
const int n,
float distortion)
{
/*
* Prevent NaNs due to input p
* Sin and Cosine are periodic about [0 2*PI) so the following
* will yield a more accurate result. As it stops the input values
* going out of range for floats which caused a NaN. The
* calculation of (px + py + pz)*5 can cause an Inf when one or more
* values are very large the cos or sin of this results in a NaN
* It also addresses the case where one dimension is large relative
* to another which caused banding due to the loss of precision in the
* smaller value. This is due to the value in the -2*PI to 2*PI range
* effectively being lost due to floating point precision.
*/
const float px = fmodf(p.x * scale, M_2PI_F);
const float py = fmodf(p.y * scale, M_2PI_F);
const float pz = fmodf(p.z * scale, M_2PI_F);
float x = sinf((px + py + pz) * 5.0f);
float y = cosf((-px + py - pz) * 5.0f);
float z = -cosf((-px - py + pz) * 5.0f);
if (n > 0) {
x *= distortion;
y *= distortion;
z *= distortion;
y = -cosf(x - y + z);
y *= distortion;
if (n > 1) {
x = cosf(x - y - z);
x *= distortion;
if (n > 2) {
z = sinf(-x - y - z);
z *= distortion;
if (n > 3) {
x = -cosf(-x + y - z);
x *= distortion;
if (n > 4) {
y = -sinf(-x + y + z);
y *= distortion;
if (n > 5) {
y = -cosf(-x + y + z);
y *= distortion;
if (n > 6) {
x = cosf(x + y + z);
x *= distortion;
if (n > 7) {
z = sinf(x + y - z);
z *= distortion;
if (n > 8) {
x = -cosf(-x - y + z);
x *= distortion;
if (n > 9) {
y = -sinf(x - y + z);
y *= distortion;
}
}
}
}
}
}
}
}
}
}
if (distortion != 0.0f) {
distortion *= 2.0f;
x /= distortion;
y /= distortion;
z /= distortion;
}
return make_float3(0.5f - x, 0.5f - y, 0.5f - z);
}
ccl_device_noinline void svm_node_tex_magic(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexMagic &ccl_restrict node)
{
const float3 co = stack_load_float3(stack, node.co);
const float scale = stack_load(stack, node.scale);
const float distortion = stack_load(stack, node.distortion);
const float3 color = svm_magic(co, scale, node.depth, distortion);
if (stack_valid(node.fac_offset)) {
stack_store_float(stack, node.fac_offset, average(color));
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, color);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,119 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Map Range Node */
ccl_device_inline float smootherstep(const float edge0, const float edge1, float x)
{
x = clamp(safe_divide((x - edge0), (edge1 - edge0)), 0.0f, 1.0f);
return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f);
}
ccl_device_noinline void svm_node_map_range(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMapRange &ccl_restrict node)
{
const float value = stack_load(stack, node.value);
const float from_min = stack_load(stack, node.from_min);
const float from_max = stack_load(stack, node.from_max);
const float to_min = stack_load(stack, node.to_min);
const float to_max = stack_load(stack, node.to_max);
const float steps = stack_load(stack, node.steps);
float result;
if (from_max != from_min) {
float factor = value;
switch (node.range_type) {
default:
case NODE_MAP_RANGE_LINEAR:
factor = (value - from_min) / (from_max - from_min);
break;
case NODE_MAP_RANGE_STEPPED: {
factor = (value - from_min) / (from_max - from_min);
factor = (steps > 0.0f) ? floorf(factor * (steps + 1.0f)) / steps : 0.0f;
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
factor = (from_min > from_max) ? 1.0f - smoothstep(from_max, from_min, factor) :
smoothstep(from_min, from_max, factor);
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
factor = (from_min > from_max) ? 1.0f - smootherstep(from_max, from_min, factor) :
smootherstep(from_min, from_max, factor);
break;
}
}
result = to_min + factor * (to_max - to_min);
}
else {
result = 0.0f;
}
stack_store_float(stack, node.result_offset, result);
}
ccl_device_noinline void svm_node_vector_map_range(
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeVectorMapRange &ccl_restrict node)
{
const float3 value = stack_load(stack, node.value);
const float3 from_min = stack_load(stack, node.from_min);
const float3 from_max = stack_load(stack, node.from_max);
const float3 to_min = stack_load(stack, node.to_min);
const float3 to_max = stack_load(stack, node.to_max);
const float3 steps = stack_load(stack, node.steps);
const int use_clamp = (node.range_type == NODE_MAP_RANGE_SMOOTHSTEP ||
node.range_type == NODE_MAP_RANGE_SMOOTHERSTEP) ?
0 :
node.use_clamp;
float3 result;
float3 factor = value;
switch (node.range_type) {
default:
case NODE_MAP_RANGE_LINEAR:
factor = safe_divide((value - from_min), (from_max - from_min));
break;
case NODE_MAP_RANGE_STEPPED: {
factor = safe_divide((value - from_min), (from_max - from_min));
factor = make_float3((steps.x > 0.0f) ? floorf(factor.x * (steps.x + 1.0f)) / steps.x : 0.0f,
(steps.y > 0.0f) ? floorf(factor.y * (steps.y + 1.0f)) / steps.y : 0.0f,
(steps.z > 0.0f) ? floorf(factor.z * (steps.z + 1.0f)) / steps.z :
0.0f);
break;
}
case NODE_MAP_RANGE_SMOOTHSTEP: {
factor = safe_divide((value - from_min), (from_max - from_min));
factor = clamp(factor, zero_float3(), one_float3());
factor = (make_float3(3.0f, 3.0f, 3.0f) - 2.0f * factor) * (factor * factor);
break;
}
case NODE_MAP_RANGE_SMOOTHERSTEP: {
factor = safe_divide((value - from_min), (from_max - from_min));
factor = clamp(factor, zero_float3(), one_float3());
factor = factor * factor * factor * (factor * (factor * 6.0f - 15.0f) + 10.0f);
break;
}
}
result = to_min + factor * (to_max - to_min);
if (use_clamp > 0) {
result.x = (to_min.x > to_max.x) ? clamp(result.x, to_max.x, to_min.x) :
clamp(result.x, to_min.x, to_max.x);
result.y = (to_min.y > to_max.y) ? clamp(result.y, to_max.y, to_min.y) :
clamp(result.y, to_min.y, to_max.y);
result.z = (to_min.z > to_max.z) ? clamp(result.z, to_max.z, to_min.z) :
clamp(result.z, to_min.z, to_max.z);
}
stack_store_float3(stack, node.result_offset, result);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,54 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/mapping_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Mapping Node */
template<typename Float3Type>
ccl_device_noinline void svm_node_mapping(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMapping &ccl_restrict node)
{
const float3 location = stack_load(stack, node.location);
const float3 rotation = stack_load(stack, node.rotation);
const float3 scale = stack_load(stack, node.scale);
const Float3Type vector = stack_load<Float3Type>(stack, node.vector);
const Float3Type result = svm_mapping(node.mapping_type, vector, location, rotation, scale);
stack_store(stack, node.result_offset, result);
}
/* Texture Mapping */
template<typename Float3Type>
ccl_device_noinline void svm_node_texture_mapping(
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTextureMapping &ccl_restrict node)
{
const Float3Type v = stack_load<Float3Type>(stack, node.vec_offset);
const Transform tfm = make_transform(node.tfm);
const Float3Type r = transform_point(&tfm, v);
stack_store(stack, node.out_offset, r);
}
ccl_device_noinline void svm_node_min_max(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMinMax &ccl_restrict node)
{
const float3 v = stack_load_float3(stack, node.vec_offset);
const float3 mn = node.mn;
const float3 mx = node.mx;
const float3 r = min(max(mn, v), mx);
stack_store_float3(stack, node.out_offset, r);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,38 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/types.h"
#include "util/math.h"
#include "util/transform.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
template<class Float3Type>
ccl_device Float3Type svm_mapping(NodeMappingType type,
const Float3Type vector,
const float3 location,
const float3 rotation,
const float3 scale)
{
const Transform rotationTransform = euler_to_transform(rotation);
switch (type) {
case NODE_MAPPING_TYPE_POINT:
return transform_direction(&rotationTransform, (vector * scale)) + location;
case NODE_MAPPING_TYPE_TEXTURE:
return safe_divide(transform_direction_transposed(&rotationTransform, (vector - location)),
scale);
case NODE_MAPPING_TYPE_VECTOR:
return transform_direction(&rotationTransform, (vector * scale));
case NODE_MAPPING_TYPE_NORMAL:
return safe_normalize(transform_direction(&rotationTransform, safe_divide(vector, scale)));
default:
return Float3Type(zero_float3());
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/math_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_math(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMath &ccl_restrict node)
{
const float a = stack_load(stack, node.value1);
const float b = stack_load(stack, node.value2);
const float c = stack_load(stack, node.value3);
const float result = svm_math(node.math_type, a, b, c);
stack_store_float(stack, node.result_offset, result);
}
template<typename Float3Type>
ccl_device_noinline void svm_node_vector_math(
ccl_private float *ccl_restrict stack, const ccl_global SVMNodeVectorMath &ccl_restrict node)
{
using FloatType = dual_scalar_t<Float3Type>;
const Float3Type a = stack_load<Float3Type>(stack, node.a);
const Float3Type b = stack_load<Float3Type>(stack, node.b);
const Float3Type c = stack_load<Float3Type>(stack, node.c);
const FloatType param1 = stack_load<FloatType>(stack, node.param1);
FloatType value = make_zero<FloatType>();
Float3Type vector = make_zero<Float3Type>();
svm_vector_math(&value, &vector, node.math_type, a, b, c, param1);
if (stack_valid(node.value_offset)) {
stack_store(stack, node.value_offset, value);
}
if (stack_valid(node.vector_offset)) {
stack_store(stack, node.vector_offset, vector);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,282 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/types.h"
#include "kernel/tables.h"
#include "util/math.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
template<class Float3Type, class FloatType>
ccl_device void svm_vector_math(ccl_private FloatType *value,
ccl_private Float3Type *vector,
NodeVectorMathType type,
const Float3Type a,
const Float3Type b,
const Float3Type c,
const FloatType param1)
{
switch (type) {
case NODE_VECTOR_MATH_ADD:
*vector = a + b;
break;
case NODE_VECTOR_MATH_SUBTRACT:
*vector = a - b;
break;
case NODE_VECTOR_MATH_MULTIPLY:
*vector = a * b;
break;
case NODE_VECTOR_MATH_DIVIDE:
*vector = safe_divide(a, b);
break;
case NODE_VECTOR_MATH_CROSS_PRODUCT:
*vector = cross(a, b);
break;
case NODE_VECTOR_MATH_PROJECT:
*vector = project(a, b);
break;
case NODE_VECTOR_MATH_REFLECT:
*vector = reflect(a, safe_normalize(b));
break;
case NODE_VECTOR_MATH_REFRACT:
*vector = refract(a, safe_normalize(b), param1);
break;
case NODE_VECTOR_MATH_FACEFORWARD:
*vector = faceforward(a, b, c);
break;
case NODE_VECTOR_MATH_MULTIPLY_ADD:
*vector = a * b + c;
break;
case NODE_VECTOR_MATH_DOT_PRODUCT:
*value = dot(a, b);
break;
case NODE_VECTOR_MATH_DISTANCE:
*value = distance(a, b);
break;
case NODE_VECTOR_MATH_LENGTH:
*value = len(a);
break;
case NODE_VECTOR_MATH_SCALE:
*vector = a * param1;
break;
case NODE_VECTOR_MATH_NORMALIZE:
*vector = safe_normalize(a);
break;
case NODE_VECTOR_MATH_SNAP:
*vector = floor(safe_divide(a, b)) * b;
break;
case NODE_VECTOR_MATH_ROUND:
*vector = floor(a + 0.5f);
break;
case NODE_VECTOR_MATH_FLOOR:
*vector = floor(a);
break;
case NODE_VECTOR_MATH_CEIL:
*vector = ceil(a);
break;
case NODE_VECTOR_MATH_MODULO:
*vector = safe_fmod(a, b);
break;
case NODE_VECTOR_MATH_WRAP:
*vector = wrap(a, b, c);
break;
case NODE_VECTOR_MATH_FRACTION:
*vector = a - floor(a);
break;
case NODE_VECTOR_MATH_ABSOLUTE:
*vector = fabs(a);
break;
case NODE_VECTOR_MATH_POWER:
*vector = safe_pow(a, b);
break;
case NODE_VECTOR_MATH_SIGN:
*vector = compatible_sign(a);
break;
case NODE_VECTOR_MATH_MINIMUM:
*vector = min(a, b);
break;
case NODE_VECTOR_MATH_MAXIMUM:
*vector = max(a, b);
break;
case NODE_VECTOR_MATH_SINE:
*vector = sin(a);
break;
case NODE_VECTOR_MATH_COSINE:
*vector = cos(a);
break;
case NODE_VECTOR_MATH_TANGENT:
*vector = tan(a);
break;
default:
*vector = Float3Type(zero_float3());
*value = FloatType(0.0f);
}
}
ccl_device float svm_math(NodeMathType type, const float a, float b, const float c)
{
switch (type) {
case NODE_MATH_ADD:
return a + b;
case NODE_MATH_SUBTRACT:
return a - b;
case NODE_MATH_MULTIPLY:
return a * b;
case NODE_MATH_DIVIDE:
return safe_divide(a, b);
case NODE_MATH_POWER:
return safe_powf(a, b);
case NODE_MATH_LOGARITHM:
return safe_logf(a, b);
case NODE_MATH_SQRT:
return safe_sqrtf(a);
case NODE_MATH_INV_SQRT:
return inversesqrtf(a);
case NODE_MATH_ABSOLUTE:
return fabsf(a);
case NODE_MATH_RADIANS:
return a * (M_PI_F / 180.0f);
case NODE_MATH_DEGREES:
return a * (180.0f / M_PI_F);
case NODE_MATH_MINIMUM:
return fminf(a, b);
case NODE_MATH_MAXIMUM:
return fmaxf(a, b);
case NODE_MATH_LESS_THAN:
return a < b;
case NODE_MATH_GREATER_THAN:
return a > b;
case NODE_MATH_ROUND:
return floorf(a + 0.5f);
case NODE_MATH_FLOOR:
return floorf(a);
case NODE_MATH_CEIL:
return ceilf(a);
case NODE_MATH_FRACTION:
return a - floorf(a);
case NODE_MATH_MODULO:
return safe_modulo(a, b);
case NODE_MATH_FLOORED_MODULO:
return safe_floored_modulo(a, b);
case NODE_MATH_TRUNC:
return a >= 0.0f ? floorf(a) : ceilf(a);
case NODE_MATH_SNAP:
return floorf(safe_divide(a, b)) * b;
case NODE_MATH_WRAP:
return wrapf(a, b, c);
case NODE_MATH_PINGPONG:
return pingpongf(a, b);
case NODE_MATH_SINE:
return sinf(a);
case NODE_MATH_COSINE:
return cosf(a);
case NODE_MATH_TANGENT:
return tanf(a);
case NODE_MATH_SINH:
return sinhf(a);
case NODE_MATH_COSH:
return coshf(a);
case NODE_MATH_TANH:
return tanhf(a);
case NODE_MATH_ARCSINE:
return safe_asinf(a);
case NODE_MATH_ARCCOSINE:
return safe_acosf(a);
case NODE_MATH_ARCTANGENT:
return atanf(a);
case NODE_MATH_ARCTAN2:
return compatible_atan2(a, b);
case NODE_MATH_SIGN:
return compatible_signf(a);
case NODE_MATH_EXPONENT:
return expf(a);
case NODE_MATH_COMPARE:
return ((a == b) || (fabsf(a - b) <= fmaxf(c, FLT_EPSILON))) ? 1.0f : 0.0f;
case NODE_MATH_MULTIPLY_ADD:
return a * b + c;
case NODE_MATH_SMOOTH_MIN:
return smoothminf(a, b, c);
case NODE_MATH_SMOOTH_MAX:
return -smoothminf(-a, -b, c);
default:
return 0.0f;
}
}
ccl_device float3 svm_math_blackbody_color_rec709(const float t)
{
/* Calculate color in range 800..12000 using an approximation
* a/x+bx+c for R and G and ((at + b)t + c)t + d) for B.
*
* The result of this can be negative to support gamut wider than
* than rec.709, just needs to be clamped. */
if (t >= 12000.0f) {
return make_float3(0.8262954810464208f, 0.9945080501520986f, 1.566307710274283f);
}
if (t < 800.0f) {
/* Arbitrary lower limit where light is very dim, matching OSL. */
return make_float3(5.413294490189271f, -0.20319390035873933f, -0.0822535242887164f);
}
const int i = (t >= 6365.0f) ? 6 :
(t >= 3315.0f) ? 5 :
(t >= 1902.0f) ? 4 :
(t >= 1449.0f) ? 3 :
(t >= 1167.0f) ? 2 :
(t >= 965.0f) ? 1 :
0;
ccl_constant float *r = blackbody_table_r[i];
ccl_constant float *g = blackbody_table_g[i];
ccl_constant float *b = blackbody_table_b[i];
const float t_inv = 1.0f / t;
return make_float3(r[0] * t_inv + r[1] * t + r[2],
g[0] * t_inv + g[1] * t + g[2],
((b[0] * t + b[1]) * t + b[2]) * t + b[3]);
}
ccl_device_inline float3 svm_math_gamma_color(float3 color, const float gamma)
{
if (gamma == 0.0f) {
return make_float3(1.0f, 1.0f, 1.0f);
}
if (color.x > 0.0f) {
color.x = powf(color.x, gamma);
}
if (color.y > 0.0f) {
color.y = powf(color.y, gamma);
}
if (color.z > 0.0f) {
color.z = powf(color.z, gamma);
}
return color;
}
ccl_device float3 svm_math_wavelength_color_xyz(const float lambda_nm)
{
float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); // scaled 0..80
const int i = float_to_int(ii);
float3 color;
if (i < 0 || i >= 80) {
color = make_float3(0.0f, 0.0f, 0.0f);
}
else {
ii -= i;
ccl_constant float *c = cie_color_match[i];
color = interp(make_float3(c[0], c[1], c[2]), make_float3(c[3], c[4], c[5]), ii);
}
return color;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,83 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/color_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Node */
ccl_device_noinline void svm_node_mix(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMix &ccl_restrict node)
{
const float fac = stack_load(stack, node.fac);
const float3 c1 = stack_load(stack, node.c1);
const float3 c2 = stack_load(stack, node.c2);
const float3 result = svm_mix_clamped_factor(node.mix_type, fac, c1, c2);
stack_store_float3(stack, node.result_offset, result);
}
ccl_device_noinline void svm_node_mix_color(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMixColor &ccl_restrict node)
{
float t = stack_load(stack, node.fac);
if (node.use_clamp > 0) {
t = saturatef(t);
}
const float3 a = stack_load(stack, node.a);
const float3 b = stack_load(stack, node.b);
float3 result = svm_mix(node.blend_type, t, a, b);
if (node.use_clamp_result) {
result = saturate(result);
}
stack_store_float3(stack, node.result_offset, result);
}
ccl_device_noinline void svm_node_mix_float(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMixFloat &ccl_restrict node)
{
float t = stack_load(stack, node.fac);
if (node.use_clamp > 0) {
t = saturatef(t);
}
const float a = stack_load(stack, node.a);
const float b = stack_load(stack, node.b);
const float result = a * (1 - t) + b * t;
stack_store_float(stack, node.result_offset, result);
}
ccl_device_noinline void svm_node_mix_vector(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMixVector &ccl_restrict node)
{
float t = stack_load(stack, node.fac);
if (node.use_clamp > 0) {
t = saturatef(t);
}
const float3 a = stack_load(stack, node.a);
const float3 b = stack_load(stack, node.b);
const float3 result = a * (one_float3() - t) + b * t;
stack_store_float3(stack, node.result_offset, result);
}
ccl_device_noinline void svm_node_mix_vector_non_uniform(
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeMixVectorNonUniform &ccl_restrict node)
{
float3 t = stack_load(stack, node.fac);
if (node.use_clamp > 0) {
t = saturate(t);
}
const float3 a = stack_load(stack, node.a);
const float3 b = stack_load(stack, node.b);
const float3 result = a * (one_float3() - t) + b * t;
stack_store_float3(stack, node.result_offset, result);
}
CCL_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,114 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifndef SHADER_NODE_TYPE
# define SHADER_NODE_TYPE(name)
#endif
#ifndef SHADER_NODE_TYPE_DERIVATIVE
# define SHADER_NODE_TYPE_DERIVATIVE(name) SHADER_NODE_TYPE(name)
#endif
/* NOTE: For good performance with jump tables on some GPU backends, the enum must
* match the switch order in `svm.h`. It is also assumed the derivative variation
* directly follows the regular node type. */
SHADER_NODE_TYPE(NODE_END)
SHADER_NODE_TYPE(NODE_SHADER_JUMP)
SHADER_NODE_TYPE(NODE_CLOSURE_BSDF)
SHADER_NODE_TYPE(NODE_CLOSURE_EMISSION)
SHADER_NODE_TYPE(NODE_CLOSURE_BACKGROUND)
SHADER_NODE_TYPE(NODE_CLOSURE_SET_WEIGHT)
SHADER_NODE_TYPE(NODE_CLOSURE_WEIGHT)
SHADER_NODE_TYPE(NODE_EMISSION_WEIGHT)
SHADER_NODE_TYPE(NODE_MIX_CLOSURE)
SHADER_NODE_TYPE(NODE_JUMP_IF_ZERO)
SHADER_NODE_TYPE(NODE_JUMP_IF_ONE)
SHADER_NODE_TYPE_DERIVATIVE(NODE_GEOMETRY)
SHADER_NODE_TYPE_DERIVATIVE(NODE_CONVERT)
SHADER_NODE_TYPE_DERIVATIVE(NODE_TEX_COORD)
SHADER_NODE_TYPE_DERIVATIVE(NODE_VALUE_F)
SHADER_NODE_TYPE_DERIVATIVE(NODE_VALUE_V)
SHADER_NODE_TYPE_DERIVATIVE(NODE_ATTR)
SHADER_NODE_TYPE_DERIVATIVE(NODE_VERTEX_COLOR)
SHADER_NODE_TYPE(NODE_SET_DISPLACEMENT)
SHADER_NODE_TYPE(NODE_DISPLACEMENT)
SHADER_NODE_TYPE(NODE_VECTOR_DISPLACEMENT)
SHADER_NODE_TYPE_DERIVATIVE(NODE_TEX_IMAGE)
SHADER_NODE_TYPE_DERIVATIVE(NODE_TEX_IMAGE_BOX)
SHADER_NODE_TYPE(NODE_TEX_NOISE)
SHADER_NODE_TYPE(NODE_SET_BUMP)
SHADER_NODE_TYPE(NODE_CLOSURE_SET_NORMAL)
SHADER_NODE_TYPE(NODE_ENTER_BUMP_EVAL)
SHADER_NODE_TYPE(NODE_LEAVE_BUMP_EVAL)
SHADER_NODE_TYPE(NODE_HSV)
SHADER_NODE_TYPE(NODE_CLOSURE_HOLDOUT)
SHADER_NODE_TYPE(NODE_FRESNEL)
SHADER_NODE_TYPE(NODE_LAYER_WEIGHT)
SHADER_NODE_TYPE(NODE_CLOSURE_VOLUME)
SHADER_NODE_TYPE(NODE_VOLUME_COEFFICIENTS)
SHADER_NODE_TYPE(NODE_PRINCIPLED_VOLUME)
SHADER_NODE_TYPE(NODE_MATH)
SHADER_NODE_TYPE_DERIVATIVE(NODE_VECTOR_MATH)
SHADER_NODE_TYPE(NODE_RGB_RAMP)
SHADER_NODE_TYPE(NODE_GAMMA)
SHADER_NODE_TYPE(NODE_BRIGHTCONTRAST)
SHADER_NODE_TYPE(NODE_LIGHT_PATH)
SHADER_NODE_TYPE(NODE_OBJECT_INFO)
SHADER_NODE_TYPE(NODE_PARTICLE_INFO)
SHADER_NODE_TYPE(NODE_HAIR_INFO)
SHADER_NODE_TYPE(NODE_POINT_INFO)
SHADER_NODE_TYPE_DERIVATIVE(NODE_TEXTURE_MAPPING)
SHADER_NODE_TYPE_DERIVATIVE(NODE_MAPPING)
SHADER_NODE_TYPE(NODE_MIN_MAX)
SHADER_NODE_TYPE(NODE_CAMERA)
SHADER_NODE_TYPE_DERIVATIVE(NODE_TEX_ENVIRONMENT)
SHADER_NODE_TYPE(NODE_TEX_SKY)
SHADER_NODE_TYPE(NODE_TEX_GRADIENT)
SHADER_NODE_TYPE(NODE_TEX_VORONOI)
SHADER_NODE_TYPE(NODE_TEX_GABOR)
SHADER_NODE_TYPE(NODE_TEX_WAVE)
SHADER_NODE_TYPE(NODE_TEX_MAGIC)
SHADER_NODE_TYPE(NODE_TEX_CHECKER)
SHADER_NODE_TYPE(NODE_TEX_BRICK)
SHADER_NODE_TYPE(NODE_TEX_WHITE_NOISE)
SHADER_NODE_TYPE(NODE_NORMAL)
SHADER_NODE_TYPE(NODE_LIGHT_FALLOFF)
SHADER_NODE_TYPE(NODE_IES)
SHADER_NODE_TYPE(NODE_CURVES)
SHADER_NODE_TYPE_DERIVATIVE(NODE_TANGENT)
SHADER_NODE_TYPE(NODE_NORMAL_MAP)
SHADER_NODE_TYPE(NODE_RADIAL_TILING)
SHADER_NODE_TYPE(NODE_INVERT)
SHADER_NODE_TYPE(NODE_MIX)
SHADER_NODE_TYPE(NODE_SEPARATE_COLOR)
SHADER_NODE_TYPE(NODE_COMBINE_COLOR)
SHADER_NODE_TYPE_DERIVATIVE(NODE_SEPARATE_VECTOR)
SHADER_NODE_TYPE_DERIVATIVE(NODE_COMBINE_VECTOR)
SHADER_NODE_TYPE(NODE_VECTOR_ROTATE)
SHADER_NODE_TYPE(NODE_VECTOR_TRANSFORM)
SHADER_NODE_TYPE(NODE_WIREFRAME)
SHADER_NODE_TYPE(NODE_WAVELENGTH)
SHADER_NODE_TYPE(NODE_BLACKBODY)
SHADER_NODE_TYPE(NODE_MAP_RANGE)
SHADER_NODE_TYPE(NODE_VECTOR_MAP_RANGE)
SHADER_NODE_TYPE(NODE_CLAMP)
SHADER_NODE_TYPE(NODE_BEVEL)
SHADER_NODE_TYPE(NODE_AMBIENT_OCCLUSION)
SHADER_NODE_TYPE(NODE_RAYCAST)
SHADER_NODE_TYPE(NODE_AOV_START)
SHADER_NODE_TYPE(NODE_AOV_COLOR)
SHADER_NODE_TYPE(NODE_AOV_VALUE)
SHADER_NODE_TYPE(NODE_FLOAT_CURVE)
SHADER_NODE_TYPE(NODE_MIX_COLOR)
SHADER_NODE_TYPE(NODE_MIX_FLOAT)
SHADER_NODE_TYPE(NODE_MIX_VECTOR)
SHADER_NODE_TYPE(NODE_MIX_VECTOR_NON_UNIFORM)
SHADER_NODE_TYPE(NODE_SCENE_TIME)
SHADER_NODE_TYPE(NODE_NONE)
/* Padding for struct alignment. */
SHADER_NODE_TYPE(NODE_PAD1)
#undef SHADER_NODE_TYPE
#undef SHADER_NODE_TYPE_DERIVATIVE

View File

@@ -0,0 +1,761 @@
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Adapted code from Open Shading Language. */
#pragma once
#include "util/hash.h"
#include "util/math.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
/* **** Perlin Noise **** */
ccl_device float fade(const float t)
{
return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
}
ccl_device_inline float negate_if(const float val, const int condition)
{
return (condition) ? -val : val;
}
ccl_device float grad1(const int hash, const float x)
{
const int h = hash & 15;
const float g = 1 + (h & 7);
return negate_if(g, h & 8) * x;
}
ccl_device_noinline_cpu float perlin_1d(const float x)
{
int X;
const float fx = floorfrac(x, &X);
const float u = fade(fx);
return mix(grad1(hash_uint(X), fx), grad1(hash_uint(X + 1), fx - 1.0f), u);
}
/* 2D, 3D, and 4D noise can be accelerated using SSE, so we first check if
* SSE is supported, that is, if __KERNEL_SSE__ is defined. If it is not
* supported, we do a standard implementation, but if it is supported, we
* do an implementation using SSE intrinsics.
*/
#if !defined(__KERNEL_SSE__)
/* ** Standard Implementation ** */
/* Bilinear Interpolation:
*
* v2 v3
* @ + + + + @ y
* + + ^
* + + |
* + + |
* @ + + + + @ @------> x
* v0 v1
*
*/
ccl_device float bi_mix(
const float v0, const float v1, const float v2, const float v3, const float x, float y)
{
float x1 = 1.0f - x;
return (1.0f - y) * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x);
}
/* Trilinear Interpolation:
*
* v6 v7
* @ + + + + + + @
* +\ +\
* + \ + \
* + \ + \
* + \ v4 + \ v5
* + @ + + + +++ + @ z
* + + + + y ^
* v2 @ + +++ + + + @ v3 + \ |
* \ + \ + \ |
* \ + \ + \|
* \ + \ + +---------> x
* \+ \+
* @ + + + + + + @
* v0 v1
*/
ccl_device float tri_mix(const float v0,
float v1,
float v2,
float v3,
float v4,
float v5,
float v6,
float v7,
const float x,
const float y,
const float z)
{
float x1 = 1.0f - x;
float y1 = 1.0f - y;
float z1 = 1.0f - z;
return z1 * (y1 * (v0 * x1 + v1 * x) + y * (v2 * x1 + v3 * x)) +
z * (y1 * (v4 * x1 + v5 * x) + y * (v6 * x1 + v7 * x));
}
ccl_device float quad_mix(const float v0,
float v1,
float v2,
float v3,
float v4,
float v5,
float v6,
float v7,
float v8,
float v9,
float v10,
float v11,
float v12,
float v13,
float v14,
float v15,
const float x,
const float y,
const float z,
const float w)
{
return mix(tri_mix(v0, v1, v2, v3, v4, v5, v6, v7, x, y, z),
tri_mix(v8, v9, v10, v11, v12, v13, v14, v15, x, y, z),
w);
}
ccl_device float grad2(const int hash, const float x, float y)
{
int h = hash & 7;
float u = h < 4 ? x : y;
float v = 2.0f * (h < 4 ? y : x);
return negate_if(u, h & 1) + negate_if(v, h & 2);
}
ccl_device float grad3(const int hash, const float x, float y, const float z)
{
int h = hash & 15;
float u = h < 8 ? x : y;
float vt = ((h == 12) || (h == 14)) ? x : z;
float v = h < 4 ? y : vt;
return negate_if(u, h & 1) + negate_if(v, h & 2);
}
ccl_device float grad4(const int hash, const float x, float y, const float z, float w)
{
int h = hash & 31;
float u = h < 24 ? x : y;
float v = h < 16 ? y : z;
float s = h < 8 ? z : w;
return negate_if(u, h & 1) + negate_if(v, h & 2) + negate_if(s, h & 4);
}
ccl_device_noinline_cpu float perlin_2d(const float x, const float y)
{
int X;
int Y;
float fx = floorfrac(x, &X);
float fy = floorfrac(y, &Y);
float u = fade(fx);
float v = fade(fy);
float r = bi_mix(grad2(hash_uint2(X, Y), fx, fy),
grad2(hash_uint2(X + 1, Y), fx - 1.0f, fy),
grad2(hash_uint2(X, Y + 1), fx, fy - 1.0f),
grad2(hash_uint2(X + 1, Y + 1), fx - 1.0f, fy - 1.0f),
u,
v);
return r;
}
ccl_device_noinline_cpu float perlin_3d(const float x, const float y, float z)
{
int X;
int Y;
int Z;
float fx = floorfrac(x, &X);
float fy = floorfrac(y, &Y);
float fz = floorfrac(z, &Z);
float u = fade(fx);
float v = fade(fy);
float w = fade(fz);
float r = tri_mix(grad3(hash_uint3(X, Y, Z), fx, fy, fz),
grad3(hash_uint3(X + 1, Y, Z), fx - 1.0f, fy, fz),
grad3(hash_uint3(X, Y + 1, Z), fx, fy - 1.0f, fz),
grad3(hash_uint3(X + 1, Y + 1, Z), fx - 1.0f, fy - 1.0f, fz),
grad3(hash_uint3(X, Y, Z + 1), fx, fy, fz - 1.0f),
grad3(hash_uint3(X + 1, Y, Z + 1), fx - 1.0f, fy, fz - 1.0f),
grad3(hash_uint3(X, Y + 1, Z + 1), fx, fy - 1.0f, fz - 1.0f),
grad3(hash_uint3(X + 1, Y + 1, Z + 1), fx - 1.0f, fy - 1.0f, fz - 1.0f),
u,
v,
w);
return r;
}
ccl_device_noinline_cpu float perlin_4d(const float x, const float y, float z, const float w)
{
int X;
int Y;
int Z;
int W;
float fx = floorfrac(x, &X);
float fy = floorfrac(y, &Y);
float fz = floorfrac(z, &Z);
float fw = floorfrac(w, &W);
float u = fade(fx);
float v = fade(fy);
float t = fade(fz);
float s = fade(fw);
float r = quad_mix(
grad4(hash_uint4(X, Y, Z, W), fx, fy, fz, fw),
grad4(hash_uint4(X + 1, Y, Z, W), fx - 1.0f, fy, fz, fw),
grad4(hash_uint4(X, Y + 1, Z, W), fx, fy - 1.0f, fz, fw),
grad4(hash_uint4(X + 1, Y + 1, Z, W), fx - 1.0f, fy - 1.0f, fz, fw),
grad4(hash_uint4(X, Y, Z + 1, W), fx, fy, fz - 1.0f, fw),
grad4(hash_uint4(X + 1, Y, Z + 1, W), fx - 1.0f, fy, fz - 1.0f, fw),
grad4(hash_uint4(X, Y + 1, Z + 1, W), fx, fy - 1.0f, fz - 1.0f, fw),
grad4(hash_uint4(X + 1, Y + 1, Z + 1, W), fx - 1.0f, fy - 1.0f, fz - 1.0f, fw),
grad4(hash_uint4(X, Y, Z, W + 1), fx, fy, fz, fw - 1.0f),
grad4(hash_uint4(X + 1, Y, Z, W + 1), fx - 1.0f, fy, fz, fw - 1.0f),
grad4(hash_uint4(X, Y + 1, Z, W + 1), fx, fy - 1.0f, fz, fw - 1.0f),
grad4(hash_uint4(X + 1, Y + 1, Z, W + 1), fx - 1.0f, fy - 1.0f, fz, fw - 1.0f),
grad4(hash_uint4(X, Y, Z + 1, W + 1), fx, fy, fz - 1.0f, fw - 1.0f),
grad4(hash_uint4(X + 1, Y, Z + 1, W + 1), fx - 1.0f, fy, fz - 1.0f, fw - 1.0f),
grad4(hash_uint4(X, Y + 1, Z + 1, W + 1), fx, fy - 1.0f, fz - 1.0f, fw - 1.0f),
grad4(hash_uint4(X + 1, Y + 1, Z + 1, W + 1), fx - 1.0f, fy - 1.0f, fz - 1.0f, fw - 1.0f),
u,
v,
t,
s);
return r;
}
#else /* SSE is supported. */
/* ** SSE Implementation ** */
/* SSE Bilinear Interpolation:
*
* The function takes two float4 inputs:
* - p : Contains the values at the points (v0, v1, v2, v3).
* - f : Contains the values (x, y, _, _). The third and fourth values are unused.
*
* The interpolation is done in two steps:
* 1. Interpolate (v0, v1) and (v2, v3) along the x axis to get g (g0, g1).
* (v2, v3) is generated by moving v2 and v3 to the first and second
* places of the float4 using the shuffle mask <2, 3, 2, 3>. The third and
* fourth values are unused.
* 2. Interpolate g0 and g1 along the y axis to get the final value.
* g1 is generated by populating an float4 with the second value of g.
* Only the first value is important in the final float4.
*
* v1 v3 g1
* @ + + + + @ @ y
* + + (1) + (2) ^
* + + ---> + ---> final |
* + + + |
* @ + + + + @ @ @------> x
* v0 v2 g0
*
*/
ccl_device_inline float4 bi_mix(const float4 p, const float4 f)
{
const float4 g = mix(p, shuffle<2, 3, 2, 3>(p), shuffle<0>(f));
return mix(g, shuffle<1>(g), shuffle<1>(f));
}
ccl_device_inline float4 fade(const float4 t)
{
const float4 a = madd(t, make_float4(6.0f), make_float4(-15.0f));
const float4 b = madd(t, a, make_float4(10.0f));
return (t * t) * (t * b);
}
/* Negate val if the nth bit of h is 1. */
# define negate_if_nth_bit(val, h, n) ((val) ^ cast(((h) & (1 << (n))) << (31 - (n))))
ccl_device_inline float4 grad(const int4 hash, const float4 x, const float4 y)
{
const int4 h = hash & 7;
const float4 u = select(h < 4, x, y);
const float4 v = 2.0f * select(h < 4, y, x);
return negate_if_nth_bit(u, h, 0) + negate_if_nth_bit(v, h, 1);
}
/* We use SSE to compute and interpolate 4 gradients at once:
*
* Point Offset from v0
* v0 (0, 0)
* v1 (0, 1)
* v2 (1, 0) (0, 1, 0, 1) = shuffle<0, 2, 0, 2>(shuffle<1, 1, 1, 1>(V, V + 1))
* v3 (1, 1) ^
* | |__________| (0, 0, 1, 1) = shuffle<0, 0, 0, 0>(V, V + 1)
* | ^
* |__________________________|
*
*/
ccl_device_noinline_cpu float perlin_2d(const float x, const float y)
{
int4 XY;
const float4 fxy = floorfrac(make_float4(x, y, 0.0f, 0.0f), &XY);
const float4 uv = fade(fxy);
const int4 XY1 = XY + make_int4(1);
const int4 X = shuffle<0, 0, 0, 0>(XY, XY1);
const int4 Y = shuffle<0, 2, 0, 2>(shuffle<1, 1, 1, 1>(XY, XY1));
const int4 h = hash_int4_2(X, Y);
const float4 fxy1 = fxy - make_float4(1.0f);
const float4 fx = shuffle<0, 0, 0, 0>(fxy, fxy1);
const float4 fy = shuffle<0, 2, 0, 2>(shuffle<1, 1, 1, 1>(fxy, fxy1));
const float4 g = grad(h, fx, fy);
return extract<0>(bi_mix(g, uv));
}
/* SSE Trilinear Interpolation:
*
* The function takes three float4 inputs:
* - p : Contains the values at the points (v0, v1, v2, v3).
* - q : Contains the values at the points (v4, v5, v6, v7).
* - f : Contains the values (x, y, z, _). The fourth value is unused.
*
* The interpolation is done in three steps:
* 1. Interpolate p and q along the x axis to get s (s0, s1, s2, s3).
* 2. Interpolate (s0, s1) and (s2, s3) along the y axis to get g (g0, g1).
* (s2, s3) is generated by moving v2 and v3 to the first and second
* places of the float4 using the shuffle mask <2, 3, 2, 3>. The third and
* fourth values are unused.
* 3. Interpolate g0 and g1 along the z axis to get the final value.
* g1 is generated by populating an float4 with the second value of g.
* Only the first value is important in the final float4.
*
* v3 v7
* @ + + + + + + @ s3 @
* +\ +\ +\
* + \ + \ + \
* + \ + \ + \ g1
* + \ v1 + \ v5 + \ s1 @
* + @ + + + +++ + @ + @ + z
* + + + + (1) + + (2) + (3) y ^
* v2 @ + +++ + + + @ v6 + ---> s2 @ + ---> + ---> final \ |
* \ + \ + \ + + \ |
* \ + \ + \ + + \|
* \ + \ + \ + @ +---------> x
* \+ \+ \+ g0
* @ + + + + + + @ @
* v0 v4 s0
*/
ccl_device_inline float4 tri_mix(const float4 p, const float4 q, float4 f)
{
const float4 s = mix(p, q, shuffle<0>(f));
const float4 g = mix(s, shuffle<2, 3, 2, 3>(s), shuffle<1>(f));
return mix(g, shuffle<1>(g), shuffle<2>(f));
}
/* 3D and 4D noise can be accelerated using AVX, so we first check if AVX
* is supported, that is, if __KERNEL_AVX__ is defined. If it is not
* supported, we do an SSE implementation, but if it is supported,
* we do an implementation using AVX intrinsics.
*/
# if !defined(__KERNEL_AVX2__)
ccl_device_inline float4 grad(const int4 hash, const float4 x, const float4 y, const float4 z)
{
const int4 h = hash & 15;
const float4 u = select(h < 8, x, y);
const float4 vt = select((h == 12) | (h == 14), x, z);
const float4 v = select(h < 4, y, vt);
return negate_if_nth_bit(u, h, 0) + negate_if_nth_bit(v, h, 1);
}
ccl_device_inline float4
grad(const int4 hash, const float4 x, const float4 y, const float4 z, const float4 w)
{
const int4 h = hash & 31;
const float4 u = select(h < 24, x, y);
const float4 v = select(h < 16, y, z);
const float4 s = select(h < 8, z, w);
return negate_if_nth_bit(u, h, 0) + negate_if_nth_bit(v, h, 1) + negate_if_nth_bit(s, h, 2);
}
/* SSE Quadrilinear Interpolation:
*
* Quadrilinear interpolation is as simple as a linear interpolation
* between two trilinear interpolations.
*
*/
ccl_device_inline float4
quad_mix(const float4 p, const float4 q, float4 r, const float4 s, float4 f)
{
return mix(tri_mix(p, q, f), tri_mix(r, s, f), shuffle<3>(f));
}
/* We use SSE to compute and interpolate 4 gradients at once. Since we have 8
* gradients in 3D, we need to compute two sets of gradients at the points:
*
* Point Offset from v0
* v0 (0, 0, 0)
* v1 (0, 0, 1)
* v2 (0, 1, 0) (0, 1, 0, 1) = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(V, V + 1))
* v3 (0, 1, 1) ^
* | |__________| (0, 0, 1, 1) = shuffle<1, 1, 1, 1>(V, V + 1)
* | ^
* |__________________________|
*
* Point Offset from v0
* v4 (1, 0, 0)
* v5 (1, 0, 1)
* v6 (1, 1, 0)
* v7 (1, 1, 1)
*
*/
ccl_device_noinline_cpu float perlin_3d(const float x, const float y, float z)
{
int4 XYZ;
const float4 fxyz = floorfrac(make_float4(x, y, z, 0.0f), &XYZ);
const float4 uvw = fade(fxyz);
const int4 XYZ1 = XYZ + make_int4(1);
const int4 Y = shuffle<1, 1, 1, 1>(XYZ, XYZ1);
const int4 Z = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZ, XYZ1));
const int4 h1 = hash_int4_3(shuffle<0>(XYZ), Y, Z);
const int4 h2 = hash_int4_3(shuffle<0>(XYZ1), Y, Z);
const float4 fxyz1 = fxyz - make_float4(1.0f);
const float4 fy = shuffle<1, 1, 1, 1>(fxyz, fxyz1);
const float4 fz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyz, fxyz1));
const float4 g1 = grad(h1, shuffle<0>(fxyz), fy, fz);
const float4 g2 = grad(h2, shuffle<0>(fxyz1), fy, fz);
return extract<0>(tri_mix(g1, g2, uvw));
}
/* We use SSE to compute and interpolate 4 gradients at once. Since we have 16
* gradients in 4D, we need to compute four sets of gradients at the points:
*
* Point Offset from v0
* v0 (0, 0, 0, 0)
* v1 (0, 0, 1, 0)
* v2 (0, 1, 0, 0) (0, 1, 0, 1) = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(V, V + 1))
* v3 (0, 1, 1, 0) ^
* | |________| (0, 0, 1, 1) = shuffle<1, 1, 1, 1>(V, V + 1)
* | ^
* |_______________________|
*
* Point Offset from v0
* v4 (1, 0, 0, 0)
* v5 (1, 0, 1, 0)
* v6 (1, 1, 0, 0)
* v7 (1, 1, 1, 0)
*
* Point Offset from v0
* v8 (0, 0, 0, 1)
* v9 (0, 0, 1, 1)
* v10 (0, 1, 0, 1)
* v11 (0, 1, 1, 1)
*
* Point Offset from v0
* v12 (1, 0, 0, 1)
* v13 (1, 0, 1, 1)
* v14 (1, 1, 0, 1)
* v15 (1, 1, 1, 1)
*
*/
ccl_device_noinline_cpu float perlin_4d(const float x, const float y, float z, const float w)
{
int4 XYZW;
const float4 fxyzw = floorfrac(make_float4(x, y, z, w), &XYZW);
const float4 uvws = fade(fxyzw);
const int4 XYZW1 = XYZW + make_int4(1);
const int4 Y = shuffle<1, 1, 1, 1>(XYZW, XYZW1);
const int4 Z = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZW, XYZW1));
const int4 h1 = hash_int4_4(shuffle<0>(XYZW), Y, Z, shuffle<3>(XYZW));
const int4 h2 = hash_int4_4(shuffle<0>(XYZW1), Y, Z, shuffle<3>(XYZW));
const int4 h3 = hash_int4_4(shuffle<0>(XYZW), Y, Z, shuffle<3>(XYZW1));
const int4 h4 = hash_int4_4(shuffle<0>(XYZW1), Y, Z, shuffle<3>(XYZW1));
const float4 fxyzw1 = fxyzw - make_float4(1.0f);
const float4 fy = shuffle<1, 1, 1, 1>(fxyzw, fxyzw1);
const float4 fz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyzw, fxyzw1));
const float4 g1 = grad(h1, shuffle<0>(fxyzw), fy, fz, shuffle<3>(fxyzw));
const float4 g2 = grad(h2, shuffle<0>(fxyzw1), fy, fz, shuffle<3>(fxyzw));
const float4 g3 = grad(h3, shuffle<0>(fxyzw), fy, fz, shuffle<3>(fxyzw1));
const float4 g4 = grad(h4, shuffle<0>(fxyzw1), fy, fz, shuffle<3>(fxyzw1));
return extract<0>(quad_mix(g1, g2, g3, g4, uvws));
}
# else /* AVX is supported. */
/* AVX Implementation */
ccl_device_inline vfloat8 grad(const vint8 hash, const vfloat8 x, const vfloat8 y, const vfloat8 z)
{
vint8 h = hash & 15;
vfloat8 u = select(h < 8, x, y);
vfloat8 vt = select((h == 12) | (h == 14), x, z);
vfloat8 v = select(h < 4, y, vt);
return negate_if_nth_bit(u, h, 0) + negate_if_nth_bit(v, h, 1);
}
ccl_device_inline vfloat8
grad(const vint8 hash, const vfloat8 x, const vfloat8 y, const vfloat8 z, const vfloat8 w)
{
vint8 h = hash & 31;
vfloat8 u = select(h < 24, x, y);
vfloat8 v = select(h < 16, y, z);
vfloat8 s = select(h < 8, z, w);
return negate_if_nth_bit(u, h, 0) + negate_if_nth_bit(v, h, 1) + negate_if_nth_bit(s, h, 2);
}
/* SSE Quadrilinear Interpolation:
*
* The interpolation is done in two steps:
* 1. Interpolate p and q along the w axis to get s.
* 2. Trilinearly interpolate (s0, s1, s2, s3) and (s4, s5, s6, s7) to get the final
* value. (s0, s1, s2, s3) and (s4, s5, s6, s7) are generated by extracting the
* low and high float4 from s.
*
*/
ccl_device_inline float4 quad_mix(vfloat8 p, vfloat8 q, const float4 f)
{
float4 fv = shuffle<3>(f);
vfloat8 s = mix(p, q, make_vfloat8(fv, fv));
return tri_mix(low(s), high(s), f);
}
/* We use AVX to compute and interpolate 8 gradients at once.
*
* Point Offset from v0
* v0 (0, 0, 0)
* v1 (0, 0, 1) The full AVX type is computed by inserting the following
* v2 (0, 1, 0) SSE types into both the low and high parts of the AVX.
* v3 (0, 1, 1)
* v4 (1, 0, 0)
* v5 (1, 0, 1) (0, 1, 0, 1) = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(V, V + 1))
* v6 (1, 1, 0) ^
* v7 (1, 1, 1) |
* | |__________| (0, 0, 1, 1) = shuffle<1, 1, 1, 1>(V, V + 1)
* | ^
* |__________________________|
*
*/
ccl_device_noinline_cpu float perlin_3d(const float x, const float y, float z)
{
int4 XYZ;
float4 fxyz = floorfrac(make_float4(x, y, z, 0.0f), &XYZ);
float4 uvw = fade(fxyz);
int4 XYZ1 = XYZ + make_int4(1);
int4 X = shuffle<0>(XYZ);
int4 X1 = shuffle<0>(XYZ1);
int4 Y = shuffle<1, 1, 1, 1>(XYZ, XYZ1);
int4 Z = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZ, XYZ1));
vint8 h = hash_int8_3(make_vint8(X, X1), make_vint8(Y, Y), make_vint8(Z, Z));
float4 fxyz1 = fxyz - make_float4(1.0f);
float4 fx = shuffle<0>(fxyz);
float4 fx1 = shuffle<0>(fxyz1);
float4 fy = shuffle<1, 1, 1, 1>(fxyz, fxyz1);
float4 fz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyz, fxyz1));
vfloat8 g = grad(h, make_vfloat8(fx, fx1), make_vfloat8(fy, fy), make_vfloat8(fz, fz));
return extract<0>(tri_mix(low(g), high(g), uvw));
}
/* We use AVX to compute and interpolate 8 gradients at once. Since we have 16
* gradients in 4D, we need to compute two sets of gradients at the points:
*
* Point Offset from v0
* v0 (0, 0, 0, 0)
* v1 (0, 0, 1, 0) The full AVX type is computed by inserting the following
* v2 (0, 1, 0, 0) SSE types into both the low and high parts of the AVX.
* v3 (0, 1, 1, 0)
* v4 (1, 0, 0, 0)
* v5 (1, 0, 1, 0) (0, 1, 0, 1) = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(V, V + 1))
* v6 (1, 1, 0, 0) ^
* v7 (1, 1, 1, 0) |
* | |________| (0, 0, 1, 1) = shuffle<1, 1, 1, 1>(V, V + 1)
* | ^
* |_______________________|
*
* Point Offset from v0
* v8 (0, 0, 0, 1)
* v9 (0, 0, 1, 1)
* v10 (0, 1, 0, 1)
* v11 (0, 1, 1, 1)
* v12 (1, 0, 0, 1)
* v13 (1, 0, 1, 1)
* v14 (1, 1, 0, 1)
* v15 (1, 1, 1, 1)
*
*/
ccl_device_noinline_cpu float perlin_4d(const float x, const float y, float z, const float w)
{
int4 XYZW;
float4 fxyzw = floorfrac(make_float4(x, y, z, w), &XYZW);
float4 uvws = fade(fxyzw);
int4 XYZW1 = XYZW + make_int4(1);
int4 X = shuffle<0>(XYZW);
int4 X1 = shuffle<0>(XYZW1);
int4 Y = shuffle<1, 1, 1, 1>(XYZW, XYZW1);
int4 Z = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZW, XYZW1));
int4 W = shuffle<3>(XYZW);
int4 W1 = shuffle<3>(XYZW1);
vint8 h1 = hash_int8_4(make_vint8(X, X1), make_vint8(Y, Y), make_vint8(Z, Z), make_vint8(W, W));
vint8 h2 = hash_int8_4(
make_vint8(X, X1), make_vint8(Y, Y), make_vint8(Z, Z), make_vint8(W1, W1));
float4 fxyzw1 = fxyzw - make_float4(1.0f);
float4 fx = shuffle<0>(fxyzw);
float4 fx1 = shuffle<0>(fxyzw1);
float4 fy = shuffle<1, 1, 1, 1>(fxyzw, fxyzw1);
float4 fz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyzw, fxyzw1));
float4 fw = shuffle<3>(fxyzw);
float4 fw1 = shuffle<3>(fxyzw1);
vfloat8 g1 = grad(
h1, make_vfloat8(fx, fx1), make_vfloat8(fy, fy), make_vfloat8(fz, fz), make_vfloat8(fw, fw));
vfloat8 g2 = grad(h2,
make_vfloat8(fx, fx1),
make_vfloat8(fy, fy),
make_vfloat8(fz, fz),
make_vfloat8(fw1, fw1));
return extract<0>(quad_mix(g1, g2, uvws));
}
# endif
# undef negate_if_nth_bit
#endif
/* Remap the output of noise to a predictable range [-1, 1].
* The scale values were computed experimentally by the OSL developers.
*/
ccl_device_inline float noise_scale1(const float result)
{
return 0.2500f * result;
}
ccl_device_inline float noise_scale2(const float result)
{
return 0.6616f * result;
}
ccl_device_inline float noise_scale3(const float result)
{
return 0.9820f * result;
}
ccl_device_inline float noise_scale4(const float result)
{
return 0.8344f * result;
}
/* Safe Signed And Unsigned Noise */
ccl_device_inline float snoise_1d(float p)
{
const float precision_correction = 0.5f * float(fabsf(p) >= 1000000.0f);
/* Repeat Perlin noise texture every 100000.0 on each axis to prevent floating point
* representation issues. */
/* The 1D variant of fmod is called fmodf. */
p = fmodf(p, 100000.0f) + precision_correction;
return noise_scale1(perlin_1d(p));
}
ccl_device_inline float noise_1d(const float p)
{
return 0.5f * snoise_1d(p) + 0.5f;
}
ccl_device_inline float snoise_2d(float2 p)
{
const float2 precision_correction = 0.5f *
mask(fabs(p) >= make_float2(1000000.0f), one_float2());
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f) + precision_correction;
return noise_scale2(perlin_2d(p.x, p.y));
}
ccl_device_inline float noise_2d(const float2 p)
{
return 0.5f * snoise_2d(p) + 0.5f;
}
ccl_device_inline float snoise_3d(float3 p)
{
const float3 precision_correction = 0.5f *
mask(fabs(p) >= make_float3(1000000.0f), one_float3());
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f) + precision_correction;
return noise_scale3(perlin_3d(p.x, p.y, p.z));
}
ccl_device_inline float noise_3d(const float3 p)
{
return 0.5f * snoise_3d(p) + 0.5f;
}
ccl_device_inline float snoise_4d(float4 p)
{
const float4 precision_correction = 0.5f *
mask(fabs(p) >= make_float4(1000000.0f), one_float4());
/* Repeat Perlin noise texture every 100000.0f on each axis to prevent floating point
* representation issues. This causes discontinuities every 100000.0f, however at such scales
* this usually shouldn't be noticeable. */
p = fmod(p, 100000.0f) + precision_correction;
return noise_scale4(perlin_4d(p.x, p.y, p.z, p.w));
}
ccl_device_inline float noise_4d(const float4 p)
{
return 0.5f * snoise_4d(p) + 0.5f;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,336 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/fractal_noise.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* The following offset functions generate random offsets to be added to texture
* coordinates to act as a seed since the noise functions don't have seed values.
* A seed value is needed for generating distortion textures and color outputs.
* The offset's components are in the range [100, 200], not too high to cause
* bad precision and not too small to be noticeable. We use float seed because
* OSL only support float hashes.
*/
ccl_device_inline float random_float_offset(const float seed)
{
return 100.0f + hash_float_to_float(seed) * 100.0f;
}
ccl_device_inline float2 random_float2_offset(const float seed)
{
return make_float2(100.0f + hash_float2_to_float(make_float2(seed, 0.0f)) * 100.0f,
100.0f + hash_float2_to_float(make_float2(seed, 1.0f)) * 100.0f);
}
ccl_device_inline float3 random_float3_offset(const float seed)
{
return make_float3(100.0f + hash_float2_to_float(make_float2(seed, 0.0f)) * 100.0f,
100.0f + hash_float2_to_float(make_float2(seed, 1.0f)) * 100.0f,
100.0f + hash_float2_to_float(make_float2(seed, 2.0f)) * 100.0f);
}
ccl_device_inline float4 random_float4_offset(const float seed)
{
return make_float4(100.0f + hash_float2_to_float(make_float2(seed, 0.0f)) * 100.0f,
100.0f + hash_float2_to_float(make_float2(seed, 1.0f)) * 100.0f,
100.0f + hash_float2_to_float(make_float2(seed, 2.0f)) * 100.0f,
100.0f + hash_float2_to_float(make_float2(seed, 3.0f)) * 100.0f);
}
template<typename T>
ccl_device float noise_select(T p,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain,
const int type,
bool normalize)
{
switch ((NodeNoiseType)type) {
case NODE_NOISE_MULTIFRACTAL: {
return noise_multi_fractal(p, detail, roughness, lacunarity);
}
case NODE_NOISE_FBM: {
return noise_fbm(p, detail, roughness, lacunarity, normalize);
}
case NODE_NOISE_HYBRID_MULTIFRACTAL: {
return noise_hybrid_multi_fractal(p, detail, roughness, lacunarity, offset, gain);
}
case NODE_NOISE_RIDGED_MULTIFRACTAL: {
return noise_ridged_multi_fractal(p, detail, roughness, lacunarity, offset, gain);
}
case NODE_NOISE_HETERO_TERRAIN: {
return noise_hetero_terrain(p, detail, roughness, lacunarity, offset);
}
default: {
kernel_assert(0);
return 0.0;
}
}
}
ccl_device void noise_texture_1d(const float co,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain,
const float distortion,
const int type,
bool normalize,
bool color_is_needed,
ccl_private float *value,
ccl_private float3 *color)
{
float p = co;
if (distortion != 0.0f) {
p += snoise_1d(p + random_float_offset(0.0f)) * distortion;
}
*value = noise_select(p, detail, roughness, lacunarity, offset, gain, type, normalize);
if (color_is_needed) {
*color = make_float3(*value,
noise_select(p + random_float_offset(1.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize),
noise_select(p + random_float_offset(2.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize));
}
}
ccl_device void noise_texture_2d(const float2 co,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain,
const float distortion,
const int type,
const bool normalize,
const bool color_is_needed,
ccl_private float *value,
ccl_private float3 *color)
{
float2 p = co;
if (distortion != 0.0f) {
p += make_float2(snoise_2d(p + random_float2_offset(0.0f)) * distortion,
snoise_2d(p + random_float2_offset(1.0f)) * distortion);
}
*value = noise_select(p, detail, roughness, lacunarity, offset, gain, type, normalize);
if (color_is_needed) {
*color = make_float3(*value,
noise_select(p + random_float2_offset(2.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize),
noise_select(p + random_float2_offset(3.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize));
}
}
ccl_device void noise_texture_3d(const float3 co,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain,
const float distortion,
const int type,
const bool normalize,
const bool color_is_needed,
ccl_private float *value,
ccl_private float3 *color)
{
float3 p = co;
if (distortion != 0.0f) {
p += make_float3(snoise_3d(p + random_float3_offset(0.0f)) * distortion,
snoise_3d(p + random_float3_offset(1.0f)) * distortion,
snoise_3d(p + random_float3_offset(2.0f)) * distortion);
}
*value = noise_select(p, detail, roughness, lacunarity, offset, gain, type, normalize);
if (color_is_needed) {
*color = make_float3(*value,
noise_select(p + random_float3_offset(3.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize),
noise_select(p + random_float3_offset(4.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize));
}
}
ccl_device void noise_texture_4d(const float4 co,
const float detail,
const float roughness,
const float lacunarity,
const float offset,
const float gain,
const float distortion,
const int type,
const bool normalize,
const bool color_is_needed,
ccl_private float *value,
ccl_private float3 *color)
{
float4 p = co;
if (distortion != 0.0f) {
p += make_float4(snoise_4d(p + random_float4_offset(0.0f)) * distortion,
snoise_4d(p + random_float4_offset(1.0f)) * distortion,
snoise_4d(p + random_float4_offset(2.0f)) * distortion,
snoise_4d(p + random_float4_offset(3.0f)) * distortion);
}
*value = noise_select(p, detail, roughness, lacunarity, offset, gain, type, normalize);
if (color_is_needed) {
*color = make_float3(*value,
noise_select(p + random_float4_offset(4.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize),
noise_select(p + random_float4_offset(5.0f),
detail,
roughness,
lacunarity,
offset,
gain,
type,
normalize));
}
}
ccl_device_noinline void svm_node_tex_noise(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexNoise &ccl_restrict node)
{
float3 vector = stack_load_float3(stack, node.vector);
float w = stack_load(stack, node.w);
const float scale = stack_load(stack, node.scale);
float detail = stack_load(stack, node.detail);
float roughness = stack_load(stack, node.roughness);
const float lacunarity = stack_load(stack, node.lacunarity);
const float offset = stack_load(stack, node.offset);
const float gain = stack_load(stack, node.gain);
const float distortion = stack_load(stack, node.distortion);
detail = clamp(detail, 0.0f, 15.0f);
roughness = fmaxf(roughness, 0.0f);
vector *= scale;
w *= scale;
float value;
float3 color;
switch (node.dimensions) {
case 1:
noise_texture_1d(w,
detail,
roughness,
lacunarity,
offset,
gain,
distortion,
node.noise_type,
node.normalize,
stack_valid(node.color_offset),
&value,
&color);
break;
case 2:
noise_texture_2d(make_float2(vector.x, vector.y),
detail,
roughness,
lacunarity,
offset,
gain,
distortion,
node.noise_type,
node.normalize,
stack_valid(node.color_offset),
&value,
&color);
break;
case 3:
noise_texture_3d(vector,
detail,
roughness,
lacunarity,
offset,
gain,
distortion,
node.noise_type,
node.normalize,
stack_valid(node.color_offset),
&value,
&color);
break;
case 4:
noise_texture_4d(make_float4(vector, w),
detail,
roughness,
lacunarity,
offset,
gain,
distortion,
node.noise_type,
node.normalize,
stack_valid(node.color_offset),
&value,
&color);
break;
default:
kernel_assert(0);
}
if (stack_valid(node.value_offset)) {
stack_store_float(stack, node.value_offset, value);
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, color);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,29 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_normal(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeNormal &ccl_restrict node)
{
const float3 normal = stack_load(stack, node.in_normal);
float3 direction = make_float3(node.direction_x, node.direction_y, node.direction_z);
direction = normalize(direction);
if (stack_valid(node.out_normal_offset)) {
stack_store_float3(stack, node.out_normal_offset, direction);
}
if (stack_valid(node.out_dot_offset)) {
stack_store_float(stack, node.out_dot_offset, dot(direction, normalize(normal)));
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2024-2025 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Define macro flags for code adaption. */
#define ADAPT_TO_SVM
/* The rounded polygon calculation functions are defined in radial_tiling_shared.h. */
#include "radial_tiling_shared.h"
/* Undefine macro flags used for code adaption. */
#undef ADAPT_TO_SVM
template<uint node_feature_mask>
ccl_device_noinline void svm_node_radial_tiling(
ccl_private float *ccl_restrict stack, const ccl_global SVMNodeRadialTiling &ccl_restrict node)
{
const bool calculate_r_gon_parameter_field = stack_valid(node.segment_coordinates_offset);
const bool calculate_segment_id = stack_valid(node.segment_id_offset);
const bool calculate_max_unit_parameter = stack_valid(node.max_unit_parameter_offset);
const bool calculate_x_axis_A_angle_bisector = stack_valid(node.x_axis_A_angle_bisector_offset);
const float3 coord = stack_load(stack, node.vector);
const float r_gon_sides = stack_load(stack, node.r_gon_sides);
const float r_gon_roundness = stack_load(stack, node.r_gon_roundness);
if (calculate_r_gon_parameter_field || calculate_max_unit_parameter ||
calculate_x_axis_A_angle_bisector)
{
float4 out_variables = calculate_out_variables(calculate_r_gon_parameter_field,
calculate_max_unit_parameter,
node.normalize_r_gon_parameter,
fmaxf(r_gon_sides, 2.0f),
clamp(r_gon_roundness, 0.0f, 1.0f),
make_float2(coord.x, coord.y));
if (calculate_r_gon_parameter_field) {
stack_store_float3(stack,
node.segment_coordinates_offset,
make_float3(out_variables.y, out_variables.x, 0.0f));
}
if (calculate_max_unit_parameter) {
stack_store_float(stack, node.max_unit_parameter_offset, out_variables.z);
}
if (calculate_x_axis_A_angle_bisector) {
stack_store_float(stack, node.x_axis_A_angle_bisector_offset, out_variables.w);
}
}
if (calculate_segment_id) {
stack_store_float(
stack,
node.segment_id_offset,
calculate_out_segment_id(fmaxf(r_gon_sides, 2.0f), make_float2(coord.x, coord.y)));
}
}
CCL_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,156 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
ccl_device_inline float fetch_float(KernelGlobals kg, const int offset)
{
return __uint_as_float(kernel_data_fetch(svm_nodes, offset));
}
ccl_device_inline float float_ramp_lookup(KernelGlobals kg,
const int offset,
float f,
bool interpolate,
bool extrapolate,
const int table_size)
{
if ((f < 0.0f || f > 1.0f) && extrapolate) {
float t0;
float dy;
if (f < 0.0f) {
t0 = fetch_float(kg, offset);
dy = t0 - fetch_float(kg, offset + 1);
f = -f;
}
else {
t0 = fetch_float(kg, offset + table_size - 1);
dy = t0 - fetch_float(kg, offset + table_size - 2);
f = f - 1.0f;
}
return t0 + dy * f * (table_size - 1);
}
f = saturatef(f) * (table_size - 1);
/* clamp int as well in case of NaN */
const int i = clamp(float_to_int(f), 0, table_size - 1);
const float t = f - (float)i;
float a = fetch_float(kg, offset + i);
if (interpolate && t > 0.0f) {
a = (1.0f - t) * a + t * fetch_float(kg, offset + i + 1);
}
return a;
}
ccl_device_inline float4 rgb_ramp_lookup(KernelGlobals kg,
const int offset,
float f,
bool interpolate,
bool extrapolate,
const int table_size)
{
if ((f < 0.0f || f > 1.0f) && extrapolate) {
float4 t0;
float4 dy;
if (f < 0.0f) {
t0 = svm_node_get_data_float4(kg, offset);
dy = t0 - svm_node_get_data_float4(kg, offset + 4);
f = -f;
}
else {
t0 = svm_node_get_data_float4(kg, offset + (table_size - 1) * 4);
dy = t0 - svm_node_get_data_float4(kg, offset + (table_size - 2) * 4);
f = f - 1.0f;
}
return t0 + dy * f * (table_size - 1);
}
f = saturatef(f) * (table_size - 1);
/* clamp int as well in case of NaN */
const int i = clamp(float_to_int(f), 0, table_size - 1);
const float t = f - (float)i;
float4 a = svm_node_get_data_float4(kg, offset + i * 4);
if (interpolate && t > 0.0f) {
a = (1.0f - t) * a + t * svm_node_get_data_float4(kg, offset + (i + 1) * 4);
}
return a;
}
ccl_device_noinline int svm_node_rgb_ramp(KernelGlobals kg,
ccl_private float *stack,
const ccl_global SVMNodeRGBRamp &node,
int offset)
{
const float fac = stack_load(stack, node.fac);
const float4 color = rgb_ramp_lookup(kg, offset, fac, node.interpolate, false, node.table_size);
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, make_float3(color));
}
if (stack_valid(node.alpha_offset)) {
stack_store_float(stack, node.alpha_offset, color.w);
}
offset += node.table_size * 4;
return offset;
}
ccl_device_noinline int svm_node_curves(KernelGlobals kg,
ccl_private float *stack,
const ccl_global SVMNodeCurves &node,
int offset)
{
const float fac = stack_load(stack, node.fac);
float3 color = stack_load(stack, node.color);
const float range_x = node.max_x - node.min_x;
const float3 relpos = (color - make_float3(node.min_x, node.min_x, node.min_x)) / range_x;
const float r = rgb_ramp_lookup(kg, offset, relpos.x, true, node.extrapolate, node.table_size).x;
const float g = rgb_ramp_lookup(kg, offset, relpos.y, true, node.extrapolate, node.table_size).y;
const float b = rgb_ramp_lookup(kg, offset, relpos.z, true, node.extrapolate, node.table_size).z;
color = (1.0f - fac) * color + fac * make_float3(r, g, b);
stack_store_float3(stack, node.out_offset, color);
offset += node.table_size * 4;
return offset;
}
ccl_device_noinline int svm_node_curve(KernelGlobals kg,
ccl_private float *stack,
const ccl_global SVMNodeFloatCurve &node,
int offset)
{
const float fac = stack_load(stack, node.fac);
float in = stack_load(stack, node.value_in);
const float range = node.max_x - node.min_x;
const float relpos = (in - node.min_x) / range;
const float v = float_ramp_lookup(kg, offset, relpos, true, node.extrapolate, node.table_size);
in = (1.0f - fac) * in + fac * v;
stack_store_float(stack, node.out_offset, in);
offset += node.table_size;
return offset;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,80 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "util/math.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */
ccl_device_inline float3 rgb_ramp_lookup(
const packed_float3 *ramp, float f, bool interpolate, bool extrapolate, const int table_size)
{
if ((f < 0.0f || f > 1.0f) && extrapolate) {
float3 t0;
float3 dy;
if (f < 0.0f) {
t0 = ramp[0];
dy = t0 - ramp[1], f = -f;
}
else {
t0 = ramp[table_size - 1];
dy = t0 - ramp[table_size - 2];
f = f - 1.0f;
}
return t0 + dy * f * (table_size - 1);
}
f = clamp(f, 0.0f, 1.0f) * (table_size - 1);
/* clamp int as well in case of NaN */
const int i = clamp(float_to_int(f), 0, table_size - 1);
const float t = f - (float)i;
float3 result = ramp[i];
if (interpolate && t > 0.0f) {
result = (1.0f - t) * result + t * ramp[i + 1];
}
return result;
}
ccl_device float float_ramp_lookup(
const float *ramp, float f, bool interpolate, bool extrapolate, const int table_size)
{
if ((f < 0.0f || f > 1.0f) && extrapolate) {
float t0;
float dy;
if (f < 0.0f) {
t0 = ramp[0];
dy = t0 - ramp[1], f = -f;
}
else {
t0 = ramp[table_size - 1];
dy = t0 - ramp[table_size - 2];
f = f - 1.0f;
}
return t0 + dy * f * (table_size - 1);
}
f = clamp(f, 0.0f, 1.0f) * (table_size - 1);
/* clamp int as well in case of NaN */
const int i = clamp(float_to_int(f), 0, table_size - 1);
const float t = f - (float)i;
float result = ramp[i];
if (interpolate && t > 0.0f) {
result = (1.0f - t) * result + t * ramp[i + 1];
}
return result;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,195 @@
/* SPDX-FileCopyrightText: 2011-2025 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/integrator/path_state.h"
#include "kernel/bvh/bvh.h"
#include "kernel/sample/mapping.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/geom/shader_data.h"
CCL_NAMESPACE_BEGIN
#ifdef __SHADER_RAYTRACE__
ccl_device bool svm_raycast(KernelGlobals kg,
ConstIntegratorState /*state*/,
ccl_private ShaderData *sd,
const float3 position,
const float3 direction,
const float distance,
const bool only_local,
const float bump_filter_width,
ccl_private ShaderData &hit_sd)
{
/* Early out if no sampling needed. */
if (distance <= 0.0f || sd->object == OBJECT_NONE) {
return false;
}
/* Can't ray-trace from shaders like displacement, before BVH exists. */
if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) {
return false;
}
float tmin = 0.0f;
bool avoid_self_intersection = false;
if (bump_filter_width > 0.0f) {
/* If evaluating for bump mapping at a shifted position, increase min distance by slightly more
* than the shift distance to avoid self intersections. */
tmin = bump_filter_width * sd->dP * 1.1f;
}
else {
avoid_self_intersection = isequal(position, sd->P);
}
/* Create ray. */
Ray ray;
ray.P = position;
ray.D = direction;
ray.tmin = tmin;
ray.tmax = distance;
ray.time = sd->time;
ray.self.object = avoid_self_intersection ? sd->object : OBJECT_NONE;
ray.self.prim = avoid_self_intersection ? sd->prim : PRIM_NONE;
ray.self.light_object = OBJECT_NONE;
ray.self.light_prim = PRIM_NONE;
ray.dP = differential_zero_compact();
ray.dD = differential_zero_compact();
Intersection isect;
if (only_local) {
LocalIntersection local_isect;
scene_intersect_local(kg, &ray, &local_isect, sd->object, nullptr, 1);
if (local_isect.num_hits == 0) {
return false;
}
isect = local_isect.hits[0];
}
else {
/* Ray-trace, leaving out shadow opaque to avoid early exit. */
const PathRayVisibility visibility = PATH_RAY_VISIBILITY_ALL &
~PATH_RAY_VISIBILITY_SHADOW_OPAQUE;
if (!scene_intersect(kg, &ray, visibility, &isect)) {
return false;
}
}
shader_setup_from_ray(kg, &hit_sd, &ray, &isect);
return true;
}
ccl_device_inline void svm_raycast_attr_eval_and_store(
KernelGlobals kg,
ccl_private float *stack,
ccl_global const SVMNodeAttr &attribute_node,
ccl_private ShaderData &hit_sd)
{
NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT;
const AttributeDescriptor desc = svm_node_attr_init(kg, &hit_sd, attribute_node, &type);
const float3 data = svm_node_attr_surface_eval<float3>(kg, &hit_sd, attribute_node, type, desc);
svm_node_attr_store(type, stack, attribute_node.out_offset, data);
}
template<uint node_feature_mask, typename ConstIntegratorGenericState>
# if defined(__KERNEL_OPTIX__)
ccl_device_inline
# else
ccl_device_noinline
# endif
int
svm_node_raycast(KernelGlobals kg,
ConstIntegratorGenericState state,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeRaycast &ccl_restrict node,
int offset)
{
const float distance = stack_load(stack, node.distance);
float is_hit = 0.0f;
float is_self_hit = 0.0f;
float hit_distance = distance;
float3 hit_position = make_float3(0.0f);
float3 hit_normal = make_float3(0.0f);
IF_KERNEL_NODES_FEATURE(RAYTRACE)
{
const float3 position = stack_load(stack, node.position);
const float3 direction = stack_load(stack, node.direction);
ShaderDataTinyStorage hit_sd_storage;
ccl_private ShaderData &hit_sd = *AS_SHADER_DATA(&hit_sd_storage);
if (svm_raycast(kg,
state,
sd,
position,
direction,
distance,
node.only_local,
node.bump_filter_width,
hit_sd))
{
is_hit = 1.0f;
is_self_hit = (sd->object == hit_sd.object) ? 1.0f : 0.0f;
hit_distance = hit_sd.ray_length;
hit_position = position + direction * hit_distance;
hit_normal = hit_sd.N;
for (uint16_t i = 0; i < node.num_attributes; i++) {
const uint node_type = kernel_data_fetch(svm_nodes, offset++);
(void)node_type;
kernel_assert(node_type == NODE_ATTR);
const ccl_global auto &attribute_node = svm_node_get<SVMNodeAttr>(kg, &offset);
svm_raycast_attr_eval_and_store(kg, stack, attribute_node, hit_sd);
}
}
}
if (is_hit == 0.0f) {
for (uint16_t i = 0; i < node.num_attributes; i++) {
const uint node_type = kernel_data_fetch(svm_nodes, offset++);
(void)node_type;
kernel_assert(node_type == NODE_ATTR);
const ccl_global auto &attribute_node = svm_node_get<SVMNodeAttr>(kg, &offset);
svm_node_attr_store(
attribute_node.output_type, stack, attribute_node.out_offset, make_zero<float3>());
}
}
if (stack_valid(node.is_hit_offset)) {
stack_store_float(stack, node.is_hit_offset, is_hit);
}
if (stack_valid(node.is_self_hit_offset)) {
stack_store_float(stack, node.is_self_hit_offset, is_self_hit);
}
if (stack_valid(node.hit_distance_offset)) {
stack_store_float(stack, node.hit_distance_offset, hit_distance);
}
if (stack_valid(node.hit_position_offset)) {
stack_store_float3(stack, node.hit_position_offset, hit_position);
}
if (stack_valid(node.hit_normal_offset)) {
stack_store_float3(stack, node.hit_normal_offset, hit_normal);
}
return offset;
}
#endif /* __SHADER_RAYTRACE__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2026 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_scene_time(KernelGlobals kg,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeSceneTime &ccl_restrict node)
{
if (stack_valid(node.seconds_out)) {
stack_store_float(stack, node.seconds_out, kernel_data.scene_time.time);
}
if (stack_valid(node.frame_out)) {
stack_store_float(stack, node.frame_out, kernel_data.scene_time.frame);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,48 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/color_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_combine_color(
ccl_private float *ccl_restrict stack, const ccl_global SVMNodeCombineColor &ccl_restrict node)
{
const float r = stack_load(stack, node.red);
const float g = stack_load(stack, node.green);
const float b = stack_load(stack, node.blue);
/* Combine, and convert back to RGB */
const float3 color = svm_combine_color(node.color_type, make_float3(r, g, b));
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, color);
}
}
ccl_device_noinline void svm_node_separate_color(
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeSeparateColor &ccl_restrict node)
{
float3 color = stack_load(stack, node.color);
/* Convert color space */
color = svm_separate_color(node.color_type, color);
if (stack_valid(node.red_offset)) {
stack_store_float(stack, node.red_offset, color.x);
}
if (stack_valid(node.green_offset)) {
stack_store_float(stack, node.green_offset, color.y);
}
if (stack_valid(node.blue_offset)) {
stack_store_float(stack, node.blue_offset, color.z);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Vector combine / separate, used for the RGB and XYZ nodes */
template<typename Float3Type>
ccl_device void svm_node_combine_vector(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeCombineVector &ccl_restrict node)
{
using FloatType = dual_scalar_t<Float3Type>;
const FloatType value = stack_load<FloatType>(stack, node.in);
if (stack_valid(node.out_offset)) {
if constexpr (is_dual_v<Float3Type>) {
stack_store_float(stack, node.out_offset + node.vector_index, value.val);
stack_store_float(stack, node.out_offset + node.vector_index + 3, value.dx);
stack_store_float(stack, node.out_offset + node.vector_index + 6, value.dy);
}
else {
stack_store_float(stack, node.out_offset + node.vector_index, value);
}
}
}
template<typename Float3Type>
ccl_device void svm_node_separate_vector(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeSeparateVector &ccl_restrict node)
{
const Float3Type vector = stack_load<Float3Type>(stack, node.vector);
if (stack_valid(node.out_offset)) {
if constexpr (is_dual_v<Float3Type>) {
if (node.vector_index == 0) {
stack_store(stack, node.out_offset, vector.x());
}
else if (node.vector_index == 1) {
stack_store(stack, node.out_offset, vector.y());
}
else {
stack_store(stack, node.out_offset, vector.z());
}
}
else {
if (node.vector_index == 0) {
stack_store(stack, node.out_offset, vector.x);
}
else if (node.vector_index == 1) {
stack_store(stack, node.out_offset, vector.y);
}
else {
stack_store(stack, node.out_offset, vector.z);
}
}
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,265 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/image.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/types.h"
#include "kernel/svm/util.h"
#include "kernel/util/colorspace.h"
#include "kernel/util/differential.h"
#include "util/color.h"
#include "util/defines.h"
CCL_NAMESPACE_BEGIN
/* Sky texture */
ccl_device float sky_angle_between(const float thetav,
const float phiv,
const float theta,
const float phi)
{
const float cospsi = sinf(thetav) * sinf(theta) * cosf(phi - phiv) + cosf(thetav) * cosf(theta);
return safe_acosf(cospsi);
}
/*
* "A Practical Analytic Model for Daylight"
* A. J. Preetham, Peter Shirley, Brian Smits
*/
ccl_device float sky_perez_function(const ccl_private float *lam,
const float theta,
const float gamma)
{
const float ctheta = cosf(theta);
const float cgamma = cosf(gamma);
return (1.0f + lam[0] * expf(lam[1] / ctheta)) *
(1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma);
}
ccl_device float3 sky_radiance_preetham(KernelGlobals kg,
const float3 dir,
const float sunphi,
const float suntheta,
const float radiance_x,
const float radiance_y,
const float radiance_z,
ccl_private float *config_x,
ccl_private float *config_y,
ccl_private float *config_z)
{
/* convert vector to spherical coordinates */
const float2 spherical = direction_to_spherical(dir);
float theta = spherical.x;
const float phi = -spherical.y + M_PI_2_F;
/* angle between sun direction and dir */
const float gamma = sky_angle_between(theta, phi, suntheta, sunphi);
/* clamp theta to horizon */
theta = min(theta, M_PI_2_F - 0.001f);
/* compute xyY color space values */
const float x = radiance_y * sky_perez_function(config_y, theta, gamma);
const float y = radiance_z * sky_perez_function(config_z, theta, gamma);
const float Y = radiance_x * sky_perez_function(config_x, theta, gamma);
/* convert to RGB */
const float3 xyz = xyY_to_xyz(x, y, Y);
return xyz_to_rgb_clamped(kg, xyz);
}
/*
* "An Analytic Model for Full Spectral Sky-Dome Radiance"
* Lukas Hosek, Alexander Wilkie
*/
ccl_device float sky_radiance_internal(const ccl_private float *configuration,
const float theta,
const float gamma)
{
const float ctheta = cosf(theta);
const float cgamma = cosf(gamma);
const float expM = expf(configuration[4] * gamma);
const float rayM = cgamma * cgamma;
const float mieM = (1.0f + rayM) / powf((1.0f + configuration[8] * configuration[8] -
2.0f * configuration[8] * cgamma),
1.5f);
const float zenith = sqrtf(ctheta);
return (1.0f + configuration[0] * expf(configuration[1] / (ctheta + 0.01f))) *
(configuration[2] + configuration[3] * expM + configuration[5] * rayM +
configuration[6] * mieM + configuration[7] * zenith);
}
ccl_device float3 sky_radiance_hosek(KernelGlobals kg,
const float3 dir,
const float sunphi,
const float suntheta,
const float radiance_x,
const float radiance_y,
const float radiance_z,
ccl_private float *config_x,
ccl_private float *config_y,
ccl_private float *config_z)
{
/* convert vector to spherical coordinates */
const float2 spherical = direction_to_spherical(dir);
float theta = spherical.x;
const float phi = -spherical.y + M_PI_2_F;
/* angle between sun direction and dir */
const float gamma = sky_angle_between(theta, phi, suntheta, sunphi);
/* clamp theta to horizon */
theta = min(theta, M_PI_2_F - 0.001f);
/* compute xyz color space values */
const float x = sky_radiance_internal(config_x, theta, gamma) * radiance_x;
const float y = sky_radiance_internal(config_y, theta, gamma) * radiance_y;
const float z = sky_radiance_internal(config_z, theta, gamma) * radiance_z;
/* convert to RGB and adjust strength */
return xyz_to_rgb_clamped(kg, make_float3(x, y, z)) * (M_2PI_F / 683);
}
/* Nishita improved sky model */
ccl_device float3 geographical_to_direction(const float lat, const float lon)
{
return spherical_to_direction(lat - M_PI_2_F, lon - M_PI_2_F);
}
ccl_device float3 sky_radiance_nishita(KernelGlobals kg,
ccl_private ShaderData *sd,
const float3 dir,
const uint32_t path_flag,
const float3 pixel_bottom,
const float3 pixel_top,
const ccl_private float *sky_data,
const uint texture_id)
{
/* Definitions */
const float sun_elevation = sky_data[0];
const float sun_rotation = sky_data[1];
const float angular_diameter = sky_data[2];
const float sun_intensity = sky_data[3];
const float earth_intersection_angle = sky_data[4];
const bool sun_disc = (angular_diameter >= 0.0f);
float3 xyz = zero_float3();
const float2 direction = direction_to_spherical(dir);
const float3 sun_dir = spherical_to_direction(sun_elevation - M_PI_2_F, sun_rotation - M_PI_2_F);
const float sun_dir_angle = precise_angle(dir, sun_dir);
const float half_angular = angular_diameter * 0.5f;
const float dir_elevation = M_PI_2_F - direction.x;
/* If the ray is inside the Sun disc, render it, otherwise render the sky.
* Alternatively, ignore the Sun if we're evaluating the background texture. */
if (sun_disc && sun_dir_angle < half_angular && dir_elevation > earth_intersection_angle &&
!((path_flag & PATH_RAY_IMPORTANCE_BAKE) && kernel_data.background.use_sun_guiding))
{
/* Sun interpolation */
const float y = ((dir_elevation - sun_elevation) / angular_diameter) + 0.5f;
/* Limb darkening, coefficient is 0.6f */
const float limb_darkening = (1.0f -
0.6f * (1.0f - sqrtf(1.0f - sqr(sun_dir_angle / half_angular))));
xyz = mix(pixel_bottom, pixel_top, y) * sun_intensity * limb_darkening;
}
/* Sky */
const float x = fractf((-direction.y - M_PI_2_F + sun_rotation) * M_1_2PI_F);
/* Undo the non-linear transformation from the sky LUT */
const float y = copysignf(sqrtf(fabsf(dir_elevation) * M_2_PI_F), dir_elevation) * 0.5f + 0.5f;
xyz += make_float3(kernel_image_interp(kg, sd, texture_id, dual2(make_float2(x, y))));
/* Convert to RGB */
return xyz_to_rgb_clamped(kg, xyz);
}
ccl_device_noinline int svm_node_tex_sky(KernelGlobals kg,
ccl_private ShaderData *sd,
const uint32_t path_flag,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexSky &ccl_restrict node,
int offset)
{
/* Load data */
const NodeSkyType sky_type = node.sky_type;
const float3 dir = stack_load_float3(stack, node.dir_offset);
float3 rgb;
/* Preetham and Hosek share the same data */
if (sky_type == NODE_SKY_PREETHAM || sky_type == NODE_SKY_HOSEK) {
const ccl_global SVMNodeTexSkyPreethamData &preetham =
*reinterpret_cast<const ccl_global SVMNodeTexSkyPreethamData *>(
&kernel_data_fetch(svm_nodes, offset));
offset += sizeof(SVMNodeTexSkyPreethamData) / sizeof(uint);
/* Copy config arrays to private memory for GPU compatibility. */
float config_x[9], config_y[9], config_z[9];
for (int i = 0; i < 9; i++) {
config_x[i] = preetham.config_x[i];
config_y[i] = preetham.config_y[i];
config_z[i] = preetham.config_z[i];
}
/* Compute Sky */
if (sky_type == NODE_SKY_PREETHAM) {
rgb = sky_radiance_preetham(kg,
dir,
preetham.phi,
preetham.theta,
preetham.radiance_x,
preetham.radiance_y,
preetham.radiance_z,
config_x,
config_y,
config_z);
}
else {
rgb = sky_radiance_hosek(kg,
dir,
preetham.phi,
preetham.theta,
preetham.radiance_x,
preetham.radiance_y,
preetham.radiance_z,
config_x,
config_y,
config_z);
}
}
/* Nishita */
else {
const ccl_global SVMNodeTexSkyNishitaData &nishita =
*reinterpret_cast<const ccl_global SVMNodeTexSkyNishitaData *>(
&kernel_data_fetch(svm_nodes, offset));
offset += sizeof(SVMNodeTexSkyNishitaData) / sizeof(uint);
const float3 pixel_bottom = make_float3(
nishita.pixel_bottom_x, nishita.pixel_bottom_y, nishita.pixel_bottom_z);
const float3 pixel_top = make_float3(
nishita.pixel_top_x, nishita.pixel_top_y, nishita.pixel_top_z);
const float sky_data[5] = {nishita.sun_elevation,
nishita.sun_rotation,
nishita.angular_diameter,
nishita.sun_intensity,
nishita.earth_intersection_angle};
/* Compute Sky */
rgb = sky_radiance_nishita(
kg, sd, dir, path_flag, pixel_bottom, pixel_top, sky_data, nishita.texture_id);
}
stack_store_float3(stack, node.out_offset, rgb);
return offset;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,624 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
/* Shader Virtual Machine
*
* A shader is a list of nodes to be executed. These are simply read one after
* the other and executed, using an node counter. Each node and its associated
* data is encoded as one or more uint4's in a 1D texture. If the data is larger
* than an uint4, the node can increase the node counter to compensate for this.
* Floats are encoded as int and then converted to float again.
*
* Nodes write their output into a stack. All stack data in the stack is
* floats, since it's all factors, colors and vectors. The stack will be stored
* in local memory on the GPU, as it would take too many register and indexes in
* ways not known at compile time. This seems the only solution even though it
* may be slow, with two positive factors. If the same shader is being executed,
* memory access will be coalesced and cached.
*
* The result of shader execution will be a single closure. This means the
* closure type, associated label, data and weight. Sampling from multiple
* closures is supported through the mix closure node, the logic for that is
* mostly taken care of in the SVM compiler.
*/
#include "kernel/globals.h"
#include "kernel/types.h"
#include "kernel/svm/types.h"
#include "kernel/svm/util.h"
/* Nodes */
#include "kernel/svm/aov.h"
#include "kernel/svm/attribute.h"
#include "kernel/svm/blackbody.h"
#include "kernel/svm/brick.h"
#include "kernel/svm/brightness.h"
#include "kernel/svm/bump.h"
#include "kernel/svm/camera.h"
#include "kernel/svm/checker.h"
#include "kernel/svm/clamp.h"
#include "kernel/svm/closure.h"
#include "kernel/svm/convert.h"
#include "kernel/svm/displace.h"
#include "kernel/svm/fresnel.h"
#include "kernel/svm/gabor.h"
#include "kernel/svm/gamma.h"
#include "kernel/svm/geometry.h"
#include "kernel/svm/gradient.h"
#include "kernel/svm/hsv.h"
#include "kernel/svm/ies.h"
#include "kernel/svm/image.h"
#include "kernel/svm/invert.h"
#include "kernel/svm/light_path.h"
#include "kernel/svm/magic.h"
#include "kernel/svm/map_range.h"
#include "kernel/svm/mapping.h"
#include "kernel/svm/math.h"
#include "kernel/svm/mix.h"
#include "kernel/svm/noisetex.h"
#include "kernel/svm/normal.h"
#include "kernel/svm/radial_tiling.h"
#include "kernel/svm/ramp.h"
#include "kernel/svm/scene_time.h"
#include "kernel/svm/sepcomb_color.h"
#include "kernel/svm/sepcomb_vector.h"
#include "kernel/svm/sky.h"
#include "kernel/svm/tex_coord.h"
#include "kernel/svm/value.h"
#include "kernel/svm/vector_rotate.h"
#include "kernel/svm/vector_transform.h"
#include "kernel/svm/vertex_color.h"
#include "kernel/svm/voronoi.h"
#include "kernel/svm/wave.h"
#include "kernel/svm/wavelength.h"
#include "kernel/svm/white_noise.h"
#include "kernel/svm/wireframe.h"
#include "util/defines.h"
#ifdef __SHADER_RAYTRACE__
# include "kernel/svm/ao.h"
# include "kernel/svm/bevel.h"
# include "kernel/svm/raycast.h"
#endif
CCL_NAMESPACE_BEGIN
#ifdef __KERNEL_USE_DATA_CONSTANTS__
# define SVM_CASE(node) \
case node: \
if (!kernel_data_svm_usage_##node) \
break;
#else
# define SVM_CASE(node) case node:
#endif
/* Main Interpreter Loop */
template<uint node_feature_mask, ShaderType type, typename ConstIntegratorGenericState>
ccl_device void svm_eval_nodes(KernelGlobals kg,
ConstIntegratorGenericState state,
ccl_private ShaderData *sd,
ccl_global float *render_buffer,
const PathRayVisibility path_visibility,
const uint32_t path_flag)
{
float stack[SVM_STACK_SIZE];
/* Initialize to silence (false positive?) warning about uninitialized use on Windows. */
Spectrum closure_weight = zero_spectrum();
int offset = (sd->shader & SHADER_MASK) * (1 + sizeof(SVMNodeShaderJump) / sizeof(uint));
while (true) {
const uint node_type = kernel_data_fetch(svm_nodes, offset++);
switch (node_type) {
SVM_CASE(NODE_END)
return;
SVM_CASE(NODE_SHADER_JUMP)
{
const SVMNodeShaderJump jump = svm_node_get<SVMNodeShaderJump>(kg, &offset);
if (type == SHADER_TYPE_SURFACE) {
offset = jump.offset_surface;
}
else if (type == SHADER_TYPE_VOLUME) {
offset = jump.offset_volume;
}
else if (type == SHADER_TYPE_DISPLACEMENT) {
offset = jump.offset_displacement;
}
else {
return;
}
break;
}
SVM_CASE(NODE_CLOSURE_BSDF)
{
const ccl_global SVMNodeClosureBsdf &bsdf_node = svm_node_get<SVMNodeClosureBsdf>(kg,
&offset);
offset = svm_node_closure_bsdf<node_feature_mask, type>(
kg, sd, stack, closure_weight, bsdf_node, path_visibility, path_flag, offset);
}
break;
SVM_CASE(NODE_CLOSURE_EMISSION)
IF_KERNEL_NODES_FEATURE(EMISSION)
{
svm_node_closure_emission(
kg, sd, stack, closure_weight, svm_node_get<SVMNodeClosureEmission>(kg, &offset));
}
break;
SVM_CASE(NODE_CLOSURE_BACKGROUND)
IF_KERNEL_NODES_FEATURE(EMISSION)
{
svm_node_closure_background(
sd, stack, closure_weight, svm_node_get<SVMNodeClosureBackground>(kg, &offset));
}
break;
SVM_CASE(NODE_CLOSURE_SET_WEIGHT)
svm_node_closure_set_weight(&closure_weight,
svm_node_get<SVMNodeClosureSetWeight>(kg, &offset));
break;
SVM_CASE(NODE_CLOSURE_WEIGHT)
svm_node_closure_weight(
stack, &closure_weight, svm_node_get<SVMNodeClosureWeight>(kg, &offset));
break;
SVM_CASE(NODE_EMISSION_WEIGHT)
IF_KERNEL_NODES_FEATURE(EMISSION)
{
svm_node_emission_weight(
stack, &closure_weight, svm_node_get<SVMNodeEmissionWeight>(kg, &offset));
}
break;
SVM_CASE(NODE_MIX_CLOSURE)
svm_node_mix_closure(stack, svm_node_get<SVMNodeMixClosure>(kg, &offset));
break;
SVM_CASE(NODE_JUMP_IF_ZERO)
{
const SVMNodeJumpIfZero jump = svm_node_get<SVMNodeJumpIfZero>(kg, &offset);
if (stack_load_float(stack, jump.stack_offset) <= 0.0f) {
offset += jump.jump_offset;
}
}
break;
SVM_CASE(NODE_JUMP_IF_ONE)
{
const SVMNodeJumpIfOne jump = svm_node_get<SVMNodeJumpIfOne>(kg, &offset);
if (stack_load_float(stack, jump.stack_offset) >= 1.0f) {
offset += jump.jump_offset;
}
}
break;
SVM_CASE(NODE_GEOMETRY)
svm_node_geometry<float3>(kg, sd, stack, svm_node_get<SVMNodeGeometry>(kg, &offset));
break;
SVM_CASE(NODE_GEOMETRY_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_geometry<dual3>(kg, sd, stack, svm_node_get<SVMNodeGeometry>(kg, &offset));
}
break;
SVM_CASE(NODE_CONVERT)
svm_node_convert<float, float3>(kg, stack, svm_node_get<SVMNodeConvert>(kg, &offset));
break;
SVM_CASE(NODE_CONVERT_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_convert<dual1, dual3>(kg, stack, svm_node_get<SVMNodeConvert>(kg, &offset));
}
break;
SVM_CASE(NODE_TEX_COORD)
{
const ccl_global auto &node = svm_node_get<SVMNodeTexCoord>(kg, &offset);
offset = svm_node_tex_coord(kg, sd, path_visibility, stack, node, offset);
}
break;
SVM_CASE(NODE_TEX_COORD_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
const ccl_global auto &node = svm_node_get<SVMNodeTexCoord>(kg, &offset);
offset = svm_node_tex_coord_derivative(kg, sd, path_visibility, stack, node, offset);
}
break;
SVM_CASE(NODE_VALUE_F)
svm_node_value_f<float>(stack, svm_node_get<SVMNodeValueF>(kg, &offset));
break;
SVM_CASE(NODE_VALUE_F_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_value_f<dual1>(stack, svm_node_get<SVMNodeValueF>(kg, &offset));
}
break;
SVM_CASE(NODE_VALUE_V)
svm_node_value_v<float3>(stack, svm_node_get<SVMNodeValueV>(kg, &offset));
break;
SVM_CASE(NODE_VALUE_V_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_value_v<dual3>(stack, svm_node_get<SVMNodeValueV>(kg, &offset));
}
break;
SVM_CASE(NODE_ATTR)
IF_KERNEL_NODES_FEATURE(VOLUME)
{
#ifdef __VOLUME__
svm_node_attr_volume(kg, sd, stack, svm_node_get<SVMNodeAttr>(kg, &offset));
#endif
}
else {
svm_node_attr_surface(kg, sd, stack, svm_node_get<SVMNodeAttr>(kg, &offset));
}
break;
SVM_CASE(NODE_ATTR_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_attr_derivative(kg, sd, stack, svm_node_get<SVMNodeAttr>(kg, &offset));
}
break;
SVM_CASE(NODE_VERTEX_COLOR)
svm_node_vertex_color(kg, sd, stack, svm_node_get<SVMNodeVertexColor>(kg, &offset));
break;
SVM_CASE(NODE_VERTEX_COLOR_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_vertex_color_derivative(
kg, sd, stack, svm_node_get<SVMNodeVertexColor>(kg, &offset));
}
break;
SVM_CASE(NODE_SET_DISPLACEMENT)
svm_node_set_displacement<node_feature_mask>(
sd, stack, svm_node_get<SVMNodeSetDisplacement>(kg, &offset));
break;
SVM_CASE(NODE_DISPLACEMENT)
svm_node_displacement<node_feature_mask>(
kg, sd, stack, svm_node_get<SVMNodeDisplacement>(kg, &offset));
break;
SVM_CASE(NODE_VECTOR_DISPLACEMENT)
svm_node_vector_displacement<node_feature_mask>(
kg, sd, stack, svm_node_get<SVMNodeVectorDisplacement>(kg, &offset));
break;
SVM_CASE(NODE_TEX_IMAGE)
svm_node_tex_image<float3>(kg, sd, stack, svm_node_get<SVMNodeTexImage>(kg, &offset));
break;
SVM_CASE(NODE_TEX_IMAGE_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_tex_image<dual3>(kg, sd, stack, svm_node_get<SVMNodeTexImage>(kg, &offset));
}
break;
SVM_CASE(NODE_TEX_IMAGE_BOX)
svm_node_tex_image_box<float3>(kg, sd, stack, svm_node_get<SVMNodeTexImageBox>(kg, &offset));
break;
SVM_CASE(NODE_TEX_IMAGE_BOX_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_tex_image_box<dual3>(
kg, sd, stack, svm_node_get<SVMNodeTexImageBox>(kg, &offset));
}
break;
SVM_CASE(NODE_TEX_NOISE)
svm_node_tex_noise(stack, svm_node_get<SVMNodeTexNoise>(kg, &offset));
break;
SVM_CASE(NODE_SET_BUMP)
svm_node_set_bump<node_feature_mask>(
kg, sd, stack, svm_node_get<SVMNodeSetBump>(kg, &offset));
break;
SVM_CASE(NODE_CLOSURE_SET_NORMAL)
IF_KERNEL_NODES_FEATURE(BUMP)
{
svm_node_set_normal(sd, stack, svm_node_get<SVMNodeClosureSetNormal>(kg, &offset));
}
break;
SVM_CASE(NODE_ENTER_BUMP_EVAL)
IF_KERNEL_NODES_FEATURE(BUMP_STATE)
{
svm_node_enter_bump_eval(kg, sd, stack, svm_node_get<SVMNodeEnterBumpEval>(kg, &offset));
}
break;
SVM_CASE(NODE_LEAVE_BUMP_EVAL)
IF_KERNEL_NODES_FEATURE(BUMP_STATE)
{
svm_node_leave_bump_eval(sd, stack, svm_node_get<SVMNodeLeaveBumpEval>(kg, &offset));
}
break;
SVM_CASE(NODE_HSV)
svm_node_hsv(stack, svm_node_get<SVMNodeHSV>(kg, &offset));
break;
SVM_CASE(NODE_CLOSURE_HOLDOUT)
svm_node_closure_holdout(
sd, stack, closure_weight, svm_node_get<SVMNodeClosureHoldout>(kg, &offset));
break;
SVM_CASE(NODE_FRESNEL)
svm_node_fresnel(sd, stack, svm_node_get<SVMNodeFresnel>(kg, &offset));
break;
SVM_CASE(NODE_LAYER_WEIGHT)
svm_node_layer_weight(sd, stack, svm_node_get<SVMNodeLayerWeight>(kg, &offset));
break;
SVM_CASE(NODE_CLOSURE_VOLUME)
IF_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_closure_volume<type>(
kg, sd, stack, closure_weight, svm_node_get<SVMNodeClosureVolume>(kg, &offset));
}
break;
SVM_CASE(NODE_VOLUME_COEFFICIENTS)
IF_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_volume_coefficients<type>(kg,
sd,
stack,
closure_weight,
svm_node_get<SVMNodeVolumeCoefficients>(kg, &offset),
path_visibility);
}
break;
SVM_CASE(NODE_PRINCIPLED_VOLUME)
IF_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_principled_volume<type>(kg,
sd,
stack,
closure_weight,
svm_node_get<SVMNodePrincipledVolume>(kg, &offset),
path_visibility);
}
break;
SVM_CASE(NODE_MATH)
svm_node_math(stack, svm_node_get<SVMNodeMath>(kg, &offset));
break;
SVM_CASE(NODE_VECTOR_MATH)
svm_node_vector_math<float3>(stack, svm_node_get<SVMNodeVectorMath>(kg, &offset));
break;
SVM_CASE(NODE_VECTOR_MATH_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_vector_math<dual3>(stack, svm_node_get<SVMNodeVectorMath>(kg, &offset));
}
break;
SVM_CASE(NODE_RGB_RAMP)
{
const ccl_global auto &node = svm_node_get<SVMNodeRGBRamp>(kg, &offset);
offset = svm_node_rgb_ramp(kg, stack, node, offset);
}
break;
SVM_CASE(NODE_GAMMA)
svm_node_gamma(stack, svm_node_get<SVMNodeGamma>(kg, &offset));
break;
SVM_CASE(NODE_BRIGHTCONTRAST)
svm_node_brightness(stack, svm_node_get<SVMNodeBrightContrast>(kg, &offset));
break;
SVM_CASE(NODE_LIGHT_PATH)
svm_node_light_path<node_feature_mask>(kg,
state,
sd,
stack,
svm_node_get<SVMNodeLightPath>(kg, &offset),
path_visibility,
path_flag);
break;
SVM_CASE(NODE_OBJECT_INFO)
svm_node_object_info(kg, sd, stack, svm_node_get<SVMNodeObjectInfo>(kg, &offset));
break;
SVM_CASE(NODE_PARTICLE_INFO)
svm_node_particle_info(kg, sd, stack, svm_node_get<SVMNodeParticleInfo>(kg, &offset));
break;
#if defined(__HAIR__)
SVM_CASE(NODE_HAIR_INFO)
svm_node_hair_info(kg, sd, stack, svm_node_get<SVMNodeHairInfo>(kg, &offset));
break;
#endif
#if defined(__POINTCLOUD__)
SVM_CASE(NODE_POINT_INFO)
svm_node_point_info(kg, sd, stack, svm_node_get<SVMNodePointInfo>(kg, &offset));
break;
#endif
SVM_CASE(NODE_TEXTURE_MAPPING)
svm_node_texture_mapping<float3>(stack, svm_node_get<SVMNodeTextureMapping>(kg, &offset));
break;
SVM_CASE(NODE_TEXTURE_MAPPING_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_texture_mapping<dual3>(stack, svm_node_get<SVMNodeTextureMapping>(kg, &offset));
}
break;
SVM_CASE(NODE_MAPPING)
svm_node_mapping<float3>(stack, svm_node_get<SVMNodeMapping>(kg, &offset));
break;
SVM_CASE(NODE_MAPPING_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_mapping<dual3>(stack, svm_node_get<SVMNodeMapping>(kg, &offset));
}
break;
SVM_CASE(NODE_MIN_MAX)
svm_node_min_max(stack, svm_node_get<SVMNodeMinMax>(kg, &offset));
break;
SVM_CASE(NODE_CAMERA)
svm_node_camera(kg, sd, stack, svm_node_get<SVMNodeCamera>(kg, &offset));
break;
SVM_CASE(NODE_TEX_ENVIRONMENT)
svm_node_tex_environment<float3>(
kg, sd, stack, svm_node_get<SVMNodeTexEnvironment>(kg, &offset));
break;
SVM_CASE(NODE_TEX_ENVIRONMENT_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_tex_environment<dual3>(
kg, sd, stack, svm_node_get<SVMNodeTexEnvironment>(kg, &offset));
}
break;
SVM_CASE(NODE_TEX_SKY)
{
const ccl_global auto &node = svm_node_get<SVMNodeTexSky>(kg, &offset);
offset = svm_node_tex_sky(kg, sd, path_flag, stack, node, offset);
}
break;
SVM_CASE(NODE_TEX_GRADIENT)
svm_node_tex_gradient(stack, svm_node_get<SVMNodeTexGradient>(kg, &offset));
break;
SVM_CASE(NODE_TEX_VORONOI)
svm_node_tex_voronoi<node_feature_mask>(stack, svm_node_get<SVMNodeTexVoronoi>(kg, &offset));
break;
SVM_CASE(NODE_TEX_GABOR)
svm_node_tex_gabor(stack, svm_node_get<SVMNodeTexGabor>(kg, &offset));
break;
SVM_CASE(NODE_TEX_WAVE)
svm_node_tex_wave(stack, svm_node_get<SVMNodeTexWave>(kg, &offset));
break;
SVM_CASE(NODE_TEX_MAGIC)
svm_node_tex_magic(stack, svm_node_get<SVMNodeTexMagic>(kg, &offset));
break;
SVM_CASE(NODE_TEX_CHECKER)
svm_node_tex_checker(stack, svm_node_get<SVMNodeTexChecker>(kg, &offset));
break;
SVM_CASE(NODE_TEX_BRICK)
svm_node_tex_brick(stack, svm_node_get<SVMNodeTexBrick>(kg, &offset));
break;
SVM_CASE(NODE_TEX_WHITE_NOISE)
svm_node_tex_white_noise(stack, svm_node_get<SVMNodeTexWhiteNoise>(kg, &offset));
break;
SVM_CASE(NODE_NORMAL)
svm_node_normal(stack, svm_node_get<SVMNodeNormal>(kg, &offset));
break;
SVM_CASE(NODE_LIGHT_FALLOFF)
svm_node_light_falloff(sd, stack, svm_node_get<SVMNodeLightFalloff>(kg, &offset));
break;
SVM_CASE(NODE_IES)
svm_node_ies(kg, sd, stack, svm_node_get<SVMNodeIES>(kg, &offset));
break;
SVM_CASE(NODE_CURVES)
{
const ccl_global auto &node = svm_node_get<SVMNodeCurves>(kg, &offset);
offset = svm_node_curves(kg, stack, node, offset);
}
break;
SVM_CASE(NODE_TANGENT)
svm_node_tangent<float3>(kg, sd, stack, svm_node_get<SVMNodeTangent>(kg, &offset));
break;
SVM_CASE(NODE_TANGENT_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_tangent<dual3>(kg, sd, stack, svm_node_get<SVMNodeTangent>(kg, &offset));
}
break;
SVM_CASE(NODE_NORMAL_MAP)
svm_node_normal_map(kg, sd, stack, svm_node_get<SVMNodeNormalMap>(kg, &offset));
break;
SVM_CASE(NODE_RADIAL_TILING)
svm_node_radial_tiling<node_feature_mask>(stack,
svm_node_get<SVMNodeRadialTiling>(kg, &offset));
break;
SVM_CASE(NODE_INVERT)
svm_node_invert(stack, svm_node_get<SVMNodeInvert>(kg, &offset));
break;
SVM_CASE(NODE_MIX)
svm_node_mix(stack, svm_node_get<SVMNodeMix>(kg, &offset));
break;
SVM_CASE(NODE_SEPARATE_COLOR)
svm_node_separate_color(stack, svm_node_get<SVMNodeSeparateColor>(kg, &offset));
break;
SVM_CASE(NODE_COMBINE_COLOR)
svm_node_combine_color(stack, svm_node_get<SVMNodeCombineColor>(kg, &offset));
break;
SVM_CASE(NODE_SEPARATE_VECTOR)
svm_node_separate_vector<float3>(stack, svm_node_get<SVMNodeSeparateVector>(kg, &offset));
break;
SVM_CASE(NODE_SEPARATE_VECTOR_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_separate_vector<dual3>(stack, svm_node_get<SVMNodeSeparateVector>(kg, &offset));
}
break;
SVM_CASE(NODE_COMBINE_VECTOR)
svm_node_combine_vector<float3>(stack, svm_node_get<SVMNodeCombineVector>(kg, &offset));
break;
SVM_CASE(NODE_COMBINE_VECTOR_DERIVATIVE)
IF_NOT_KERNEL_NODES_FEATURE(VOLUME)
{
svm_node_combine_vector<dual3>(stack, svm_node_get<SVMNodeCombineVector>(kg, &offset));
}
break;
SVM_CASE(NODE_VECTOR_ROTATE)
svm_node_vector_rotate(stack, svm_node_get<SVMNodeVectorRotate>(kg, &offset));
break;
SVM_CASE(NODE_VECTOR_TRANSFORM)
svm_node_vector_transform(kg, sd, stack, svm_node_get<SVMNodeVectorTransform>(kg, &offset));
break;
SVM_CASE(NODE_WIREFRAME)
svm_node_wireframe(kg, sd, stack, svm_node_get<SVMNodeWireframe>(kg, &offset));
break;
SVM_CASE(NODE_WAVELENGTH)
svm_node_wavelength(kg, stack, svm_node_get<SVMNodeWavelength>(kg, &offset));
break;
SVM_CASE(NODE_BLACKBODY)
svm_node_blackbody(kg, stack, svm_node_get<SVMNodeBlackbody>(kg, &offset));
break;
SVM_CASE(NODE_MAP_RANGE)
svm_node_map_range(stack, svm_node_get<SVMNodeMapRange>(kg, &offset));
break;
SVM_CASE(NODE_VECTOR_MAP_RANGE)
svm_node_vector_map_range(stack, svm_node_get<SVMNodeVectorMapRange>(kg, &offset));
break;
SVM_CASE(NODE_CLAMP)
svm_node_clamp(stack, svm_node_get<SVMNodeClamp>(kg, &offset));
break;
#ifdef __SHADER_RAYTRACE__
SVM_CASE(NODE_BEVEL)
svm_node_bevel<node_feature_mask>(
kg, state, sd, stack, svm_node_get<SVMNodeBevel>(kg, &offset));
break;
SVM_CASE(NODE_AMBIENT_OCCLUSION)
svm_node_ao<node_feature_mask>(
kg, state, sd, stack, svm_node_get<SVMNodeAmbientOcclusion>(kg, &offset));
break;
SVM_CASE(NODE_RAYCAST)
{
const ccl_global auto &node = svm_node_get<SVMNodeRaycast>(kg, &offset);
offset = svm_node_raycast<node_feature_mask>(kg, state, sd, stack, node, offset);
}
break;
#endif
SVM_CASE(NODE_AOV_START)
if (!svm_node_aov_check(path_flag, render_buffer)) {
return;
}
break;
SVM_CASE(NODE_AOV_COLOR)
svm_node_aov_color<node_feature_mask>(
kg, sd, state, stack, svm_node_get<SVMNodeAOVColor>(kg, &offset), render_buffer);
break;
SVM_CASE(NODE_AOV_VALUE)
svm_node_aov_value<node_feature_mask>(
kg, sd, state, stack, svm_node_get<SVMNodeAOVValue>(kg, &offset), render_buffer);
break;
SVM_CASE(NODE_FLOAT_CURVE)
{
const ccl_global auto &node = svm_node_get<SVMNodeFloatCurve>(kg, &offset);
offset = svm_node_curve(kg, stack, node, offset);
}
break;
SVM_CASE(NODE_MIX_COLOR)
svm_node_mix_color(stack, svm_node_get<SVMNodeMixColor>(kg, &offset));
break;
SVM_CASE(NODE_MIX_FLOAT)
svm_node_mix_float(stack, svm_node_get<SVMNodeMixFloat>(kg, &offset));
break;
SVM_CASE(NODE_MIX_VECTOR)
svm_node_mix_vector(stack, svm_node_get<SVMNodeMixVector>(kg, &offset));
break;
SVM_CASE(NODE_MIX_VECTOR_NON_UNIFORM)
svm_node_mix_vector_non_uniform(stack,
svm_node_get<SVMNodeMixVectorNonUniform>(kg, &offset));
break;
SVM_CASE(NODE_SCENE_TIME)
svm_node_scene_time(kg, stack, svm_node_get<SVMNodeSceneTime>(kg, &offset));
break;
default:
kernel_assert(!"Unknown node type was passed to the SVM machine");
return;
}
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,419 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/camera/camera.h"
#include "kernel/geom/motion_triangle.h"
#include "kernel/geom/object.h"
#include "kernel/geom/primitive.h"
#include "kernel/svm/attribute.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/types.h"
#include "kernel/svm/util.h"
#include "util/math_base.h"
CCL_NAMESPACE_BEGIN
/* Smooth normal with screen-space derivatives for texture coordinate use.
* Returns the interpolated normal in object space, with dx/dy representing
* the per-pixel change from ray differentials. */
ccl_device_inline dual3 svm_texco_smooth_normal(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if ((sd->type & PRIMITIVE_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) {
float3 N_x, N_y;
float3 N;
if (sd->type == PRIMITIVE_TRIANGLE) {
N = triangle_smooth_normal(kg,
sd->Ng,
sd->object,
sd->object_flag,
sd->prim,
sd->u,
sd->v,
sd->du,
sd->dv,
N_x,
N_y);
}
else {
N = motion_triangle_smooth_normal(
kg, sd->Ng, sd->object, sd->prim, sd->time, sd->u, sd->v, sd->du, sd->dv, N_x, N_y);
}
if (sd->flag & SD_BACKFACING) {
N = -N;
N_x = -N_x;
N_y = -N_y;
}
if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) {
object_inverse_normal_transform(kg, sd, &N);
object_inverse_normal_transform(kg, sd, &N_x);
object_inverse_normal_transform(kg, sd, &N_y);
}
return dual3(N, N_x - N, N_y - N);
}
/* Flat normal or non-triangle: no derivative. */
float3 N = sd->N;
object_inverse_normal_transform(kg, sd, &N);
return dual3(N);
}
/* Texture Coordinate Node */
template<typename Float3Type>
ccl_device_inline Float3Type svm_texco_reflection(const ccl_private ShaderData *sd)
{
Float3Type data = shading_incoming<Float3Type>(sd);
if (sd->object != OBJECT_NONE) {
data = -reflect(data, sd->N);
}
return data;
}
template<typename Float3Type>
ccl_device_inline Float3Type svm_texco_camera(KernelGlobals kg,
const ccl_private ShaderData *sd,
const ccl_private Float3Type &P)
{
Float3Type data(P);
const Transform tfm = kernel_data.cam.worldtocamera;
if (sd->object == OBJECT_NONE) {
data = data + camera_position(kg);
}
data = transform_point(&tfm, data);
return data;
}
template<typename Float3Type>
ccl_device_noinline Float3Type svm_node_tex_coord_eval(KernelGlobals kg,
ccl_private ShaderData *sd,
const PathRayVisibility path_visibility,
const NodeTexCoord type,
ccl_private int *offset)
{
Float3Type data;
switch (type) {
case NODE_TEXCO_OBJECT:
case NODE_TEXCO_OBJECT_WITH_TRANSFORM: {
data = shading_position<Float3Type>(sd);
if (type == NODE_TEXCO_OBJECT) {
object_inverse_position_transform_if_object(kg, sd, &data);
}
else {
const Transform tfm = make_transform(svm_node_get<PackedTransform>(kg, offset));
data = transform_point(&tfm, data);
}
break;
}
case NODE_TEXCO_NORMAL: {
if constexpr (is_dual_v<Float3Type>) {
data = svm_texco_smooth_normal(kg, sd);
}
else {
data = sd->N;
object_inverse_normal_transform(kg, sd, &data);
}
break;
}
case NODE_TEXCO_CAMERA: {
const Float3Type P = shading_position<Float3Type>(sd);
data = svm_texco_camera<Float3Type>(kg, sd, P);
break;
}
case NODE_TEXCO_WINDOW: {
if ((path_visibility & PATH_RAY_VISIBILITY_CAMERA) && sd->object == OBJECT_NONE &&
kernel_data.cam.type == CAMERA_ORTHOGRAPHIC)
{
data = Float3Type(camera_world_to_ndc(kg, sd, sd->ray_P));
}
else {
data = Float3Type(camera_world_to_ndc(kg, sd, sd->P));
if constexpr (is_dual_v<Float3Type>) {
data.dx.x = 1.0f / kernel_data.cam.width;
data.dy.y = 1.0f / kernel_data.cam.height;
}
}
if constexpr (is_dual_v<Float3Type>) {
data.val.z = 0.0f;
}
else {
data.z = 0.0f;
}
break;
}
case NODE_TEXCO_REFLECTION: {
data = svm_texco_reflection<Float3Type>(sd);
break;
}
case NODE_TEXCO_DUPLI_GENERATED: {
data = Float3Type(object_dupli_generated(kg, sd->object));
break;
}
case NODE_TEXCO_DUPLI_UV: {
data = Float3Type(object_dupli_uv(kg, sd->object));
break;
}
case NODE_TEXCO_VOLUME_GENERATED: {
data = shading_position<Float3Type>(sd);
#ifdef __VOLUME__
if (sd->object != OBJECT_NONE) {
data = volume_normalized_position<Float3Type>(kg, sd, data);
}
#endif
break;
}
default:
data = make_zero<Float3Type>();
break;
}
return data;
}
ccl_device_noinline int svm_node_tex_coord(KernelGlobals kg,
ccl_private ShaderData *sd,
const PathRayVisibility path_visibility,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexCoord &ccl_restrict node,
int offset)
{
const float3 data = svm_node_tex_coord_eval<float3>(
kg, sd, path_visibility, node.texco_type, &offset);
stack_store(stack, node.out_offset, data);
return offset;
}
ccl_device_noinline int svm_node_tex_coord_derivative(
KernelGlobals kg,
ccl_private ShaderData *sd,
const PathRayVisibility path_visibility,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexCoord &ccl_restrict node,
int offset)
{
dual3 data = svm_node_tex_coord_eval<dual3>(kg, sd, path_visibility, node.texco_type, &offset);
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;
}
/* Normal texture coordinate must be normalized after bump offset, matching OSL. */
if (node.texco_type == NODE_TEXCO_NORMAL) {
data = safe_normalize(data);
}
if (node.store_derivatives) {
stack_store(stack, node.out_offset, data);
}
else {
stack_store(stack, node.out_offset, data.val);
}
return offset;
}
ccl_device_noinline void svm_node_normal_map(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeNormalMap &ccl_restrict node)
{
float3 color = stack_load(stack, node.color);
color = 2.0f * make_float3(color.x - 0.5f, color.y - 0.5f, color.z - 0.5f);
if (node.invert_green) {
color.y = -color.y;
}
const bool is_backfacing = (sd->flag & SD_BACKFACING) != 0;
float3 N;
float strength = stack_load(stack, node.strength);
bool linear_interpolate_strength = false;
if (node.space == NODE_NORMAL_MAP_TANGENT) {
/* tangent space */
if (sd->object == OBJECT_NONE || (sd->type & PRIMITIVE_TRIANGLE) == 0) {
/* Fall back to unperturbed normal. */
stack_store_float3(stack, node.normal_offset, sd->N);
return;
}
/* first try to get tangent attribute */
const AttributeDescriptor attr = find_attribute(kg, sd, node.attr);
const AttributeDescriptor attr_sign = find_attribute(kg, sd, node.attr_sign);
if (!is_attribute_found(attr) || !is_attribute_found(attr_sign)) {
/* Fall back to unperturbed normal. */
stack_store_float3(stack, node.normal_offset, sd->N);
return;
}
/* get _unnormalized_ interpolated normal and tangent */
const float3 tangent = primitive_surface_attribute<float3>(kg, sd, attr);
const float sign = primitive_surface_attribute<float>(kg, sd, attr_sign);
float3 normal;
if (sd->shader & SHADER_SMOOTH_NORMAL) {
const AttributeDescriptor attr_undisplaced_normal =
(node.use_original_base) ?
find_attribute(kg, sd->object, sd->prim, ATTR_STD_NORMAL_UNDISPLACED) :
attribute_not_found();
if (is_attribute_found(attr_undisplaced_normal)) {
normal = primitive_surface_attribute<float3>(kg, sd, attr_undisplaced_normal);
/* Can't interpolate in tangent space as the displaced normal is not used
* for the tangent frame. */
linear_interpolate_strength = true;
}
else {
normal = triangle_smooth_normal_unnormalized_object_space(kg, sd);
}
}
else {
normal = sd->Ng;
/* the normal is already inverted, which is too soon for the math here */
if (is_backfacing) {
normal = -normal;
}
object_inverse_normal_transform(kg, sd, &normal);
}
/* Apply strength in the tangent case. */
if (!linear_interpolate_strength) {
color.x *= strength;
color.y *= strength;
color.z = mix(1.0f, color.z, saturatef(strength));
}
/* apply normal map */
const float3 B = sign * cross(normal, tangent);
N = safe_normalize(to_global(color, tangent, B, normal));
/* transform to world space */
object_normal_transform(kg, sd, &N);
/* invert normal for backfacing polygons */
if (is_backfacing) {
N = -N;
}
}
else {
linear_interpolate_strength = true;
/* strange blender convention */
if (node.space == NODE_NORMAL_MAP_BLENDER_OBJECT ||
node.space == NODE_NORMAL_MAP_BLENDER_WORLD)
{
color.y = -color.y;
color.z = -color.z;
}
/* object, world space */
N = color;
if (node.space == NODE_NORMAL_MAP_OBJECT || node.space == NODE_NORMAL_MAP_BLENDER_OBJECT) {
object_normal_transform(kg, sd, &N);
}
else {
N = safe_normalize(N);
}
/* invert normal for backfacing polygons */
if (is_backfacing) {
N = -N;
}
}
/* Use simple linear interpolation if we can't do it in tangent space. */
if (linear_interpolate_strength && strength != 1.0f) {
strength = max(strength, 0.0f);
N = safe_normalize(sd->N + (N - sd->N) * strength);
}
if (is_zero(N) || !isfinite_safe(N)) {
N = sd->N;
}
stack_store_float3(stack, node.normal_offset, N);
}
template<typename Float3Type>
ccl_device_noinline void svm_node_tangent(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeTangent &ccl_restrict node)
{
const AttributeDescriptor desc = find_attribute(kg, sd, node.attr);
Float3Type tangent;
if (node.direction_type == NODE_TANGENT_UVMAP) {
/* UV map */
if (!is_attribute_found(desc)) {
stack_store(stack, node.tangent_offset, Float3Type());
return;
}
if (desc.type == NODE_ATTR_FLOAT2) {
if constexpr (is_dual_v<Float3Type>) {
tangent = make_float3(primitive_surface_attribute<dual2>(kg, sd, desc));
}
else {
tangent = make_float3(primitive_surface_attribute<float2>(kg, sd, desc));
}
}
else {
tangent = primitive_surface_attribute<Float3Type>(kg, sd, desc);
}
}
else {
/* radial */
Float3Type generated;
if (!is_attribute_found(desc)) {
generated = shading_position<Float3Type>(sd);
}
else if (desc.type == NODE_ATTR_FLOAT2) {
if constexpr (is_dual_v<Float3Type>) {
generated = make_float3(primitive_surface_attribute<dual2>(kg, sd, desc));
}
else {
generated = make_float3(primitive_surface_attribute<float2>(kg, sd, desc));
}
}
else {
generated = primitive_surface_attribute<Float3Type>(kg, sd, desc);
}
if constexpr (is_dual_v<Float3Type>) {
using FloatType = dual_scalar_t<Float3Type>;
if (node.axis == NODE_TANGENT_AXIS_X) {
tangent = make_float3(FloatType(), -(generated.z() - 0.5f), (generated.y() - 0.5f));
}
else if (node.axis == NODE_TANGENT_AXIS_Y) {
tangent = make_float3(-(generated.z() - 0.5f), FloatType(), (generated.x() - 0.5f));
}
else {
tangent = make_float3(-(generated.y() - 0.5f), (generated.x() - 0.5f), FloatType());
}
}
else {
if (node.axis == NODE_TANGENT_AXIS_X) {
tangent = make_float3(0.0f, -(generated.z - 0.5f), (generated.y - 0.5f));
}
else if (node.axis == NODE_TANGENT_AXIS_Y) {
tangent = make_float3(-(generated.z - 0.5f), 0.0f, (generated.x - 0.5f));
}
else {
tangent = make_float3(-(generated.y - 0.5f), (generated.x - 0.5f), 0.0f);
}
}
}
object_normal_transform(kg, sd, &tangent);
tangent = cross(sd->N, normalize(cross(tangent, sd->N)));
stack_store(stack, node.tangent_offset, tangent);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,579 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "util/transform.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
/* Stack */
/* Stack offset type. Stack offsets are in the range [0, SVM_STACK_SIZE]. */
using SVMStackOffset = uint8_t;
/* Store int value and stack offset. */
struct SVMInputInt {
int value;
SVMStackOffset offset;
uint8_t _pad[3];
};
/* Encodes a node float input, as either float value or a stack offset
* encoded in a NaN bit pattern. */
struct SVMInputFloat {
uint bits;
};
/* Encodes a node float input, as either float3 value or a stack offset
* encoded in a NaN bit pattern in the x component. */
struct SVMInputFloat3 {
SVMInputFloat x, y, z;
};
/* Bit mask for encoding stack offset as NaN. */
#define SVM_INPUT_STACK_OFFSET_MASK 0x7FC00000u
// NOLINTBEGIN
/* SVM stack has a fixed size */
#define SVM_STACK_SIZE 255
/* SVM stack offsets with this value indicate that it's not on the stack */
#define SVM_STACK_INVALID SVMStackOffset(255)
#define SVM_BUMP_EVAL_STATE_SIZE 10
// NOLINTEND
/* Nodes */
enum ShaderNodeType : uint {
#define SHADER_NODE_TYPE(name) name,
#define SHADER_NODE_TYPE_DERIVATIVE(name) name, name##_DERIVATIVE,
#include "node_types_template.h"
NODE_NUM
};
enum NodeAttributeOutputType : uint8_t {
NODE_ATTR_OUTPUT_FLOAT3 = 0,
NODE_ATTR_OUTPUT_FLOAT,
NODE_ATTR_OUTPUT_FLOAT_ALPHA,
};
enum NodeAttributeType : uint8_t {
NODE_ATTR_FLOAT = 0,
NODE_ATTR_FLOAT2,
NODE_ATTR_FLOAT3,
NODE_ATTR_FLOAT4,
NODE_ATTR_RGBA,
NODE_ATTR_MATRIX
};
enum NodeGeometry : uint8_t {
NODE_GEOM_P = 0,
NODE_GEOM_N,
NODE_GEOM_T,
NODE_GEOM_I,
NODE_GEOM_Ng,
NODE_GEOM_uv
};
enum NodeObjectInfo : uint {
NODE_INFO_OB_LOCATION,
NODE_INFO_OB_COLOR,
NODE_INFO_OB_ALPHA,
NODE_INFO_OB_INDEX,
NODE_INFO_MAT_INDEX,
NODE_INFO_OB_RANDOM
};
enum NodeParticleInfo : uint {
NODE_INFO_PAR_INDEX,
NODE_INFO_PAR_RANDOM,
NODE_INFO_PAR_AGE,
NODE_INFO_PAR_LIFETIME,
NODE_INFO_PAR_LOCATION,
// NODE_INFO_PAR_ROTATION,
NODE_INFO_PAR_SIZE,
NODE_INFO_PAR_VELOCITY,
NODE_INFO_PAR_ANGULAR_VELOCITY
};
enum NodeHairInfo : uint {
NODE_INFO_CURVE_IS_STRAND,
NODE_INFO_CURVE_INTERCEPT,
NODE_INFO_CURVE_LENGTH,
NODE_INFO_CURVE_THICKNESS,
NODE_INFO_CURVE_TANGENT_NORMAL,
NODE_INFO_CURVE_RANDOM,
};
enum NodePointInfo : uint {
NODE_INFO_POINT_POSITION,
NODE_INFO_POINT_RADIUS,
NODE_INFO_POINT_RANDOM,
};
enum NodeLightPath : uint {
NODE_LP_camera = 0,
NODE_LP_shadow,
NODE_LP_diffuse,
NODE_LP_glossy,
NODE_LP_singular,
NODE_LP_reflection,
NODE_LP_transmission,
NODE_LP_volume_scatter,
NODE_LP_backfacing,
NODE_LP_ray_length,
NODE_LP_ray_depth,
NODE_LP_ray_diffuse,
NODE_LP_ray_glossy,
NODE_LP_ray_transparent,
NODE_LP_ray_transmission,
NODE_LP_ray_portal,
};
enum NodeLightFalloff : uint {
NODE_LIGHT_FALLOFF_QUADRATIC,
NODE_LIGHT_FALLOFF_LINEAR,
NODE_LIGHT_FALLOFF_CONSTANT
};
enum NodeTexCoord : uint8_t {
NODE_TEXCO_NORMAL,
NODE_TEXCO_OBJECT,
NODE_TEXCO_OBJECT_WITH_TRANSFORM,
NODE_TEXCO_CAMERA,
NODE_TEXCO_WINDOW,
NODE_TEXCO_REFLECTION,
NODE_TEXCO_DUPLI_GENERATED,
NODE_TEXCO_DUPLI_UV,
NODE_TEXCO_VOLUME_GENERATED
};
enum NodeMix : uint {
NODE_MIX_BLEND = 0,
NODE_MIX_ADD,
NODE_MIX_MUL,
NODE_MIX_SUB,
NODE_MIX_SCREEN,
NODE_MIX_DIV,
NODE_MIX_DIFF,
NODE_MIX_DARK,
NODE_MIX_LIGHT,
NODE_MIX_OVERLAY,
NODE_MIX_DODGE,
NODE_MIX_BURN,
NODE_MIX_HUE,
NODE_MIX_SAT,
NODE_MIX_VAL,
NODE_MIX_COL,
NODE_MIX_SOFT,
NODE_MIX_LINEAR,
NODE_MIX_EXCLUSION,
NODE_MIX_CLAMP /* used for the clamp UI option */
};
enum NodeMathType : uint {
NODE_MATH_ADD,
NODE_MATH_SUBTRACT,
NODE_MATH_MULTIPLY,
NODE_MATH_DIVIDE,
NODE_MATH_SINE,
NODE_MATH_COSINE,
NODE_MATH_TANGENT,
NODE_MATH_ARCSINE,
NODE_MATH_ARCCOSINE,
NODE_MATH_ARCTANGENT,
NODE_MATH_POWER,
NODE_MATH_LOGARITHM,
NODE_MATH_MINIMUM,
NODE_MATH_MAXIMUM,
NODE_MATH_ROUND,
NODE_MATH_LESS_THAN,
NODE_MATH_GREATER_THAN,
NODE_MATH_MODULO,
NODE_MATH_ABSOLUTE,
NODE_MATH_ARCTAN2,
NODE_MATH_FLOOR,
NODE_MATH_CEIL,
NODE_MATH_FRACTION,
NODE_MATH_SQRT,
NODE_MATH_INV_SQRT,
NODE_MATH_SIGN,
NODE_MATH_EXPONENT,
NODE_MATH_RADIANS,
NODE_MATH_DEGREES,
NODE_MATH_SINH,
NODE_MATH_COSH,
NODE_MATH_TANH,
NODE_MATH_TRUNC,
NODE_MATH_SNAP,
NODE_MATH_WRAP,
NODE_MATH_COMPARE,
NODE_MATH_MULTIPLY_ADD,
NODE_MATH_PINGPONG,
NODE_MATH_SMOOTH_MIN,
NODE_MATH_SMOOTH_MAX,
NODE_MATH_FLOORED_MODULO,
};
enum NodeVectorMathType : uint {
NODE_VECTOR_MATH_ADD,
NODE_VECTOR_MATH_SUBTRACT,
NODE_VECTOR_MATH_MULTIPLY,
NODE_VECTOR_MATH_DIVIDE,
NODE_VECTOR_MATH_CROSS_PRODUCT,
NODE_VECTOR_MATH_PROJECT,
NODE_VECTOR_MATH_REFLECT,
NODE_VECTOR_MATH_DOT_PRODUCT,
NODE_VECTOR_MATH_DISTANCE,
NODE_VECTOR_MATH_LENGTH,
NODE_VECTOR_MATH_SCALE,
NODE_VECTOR_MATH_NORMALIZE,
NODE_VECTOR_MATH_SNAP,
NODE_VECTOR_MATH_FLOOR,
NODE_VECTOR_MATH_CEIL,
NODE_VECTOR_MATH_MODULO,
NODE_VECTOR_MATH_FRACTION,
NODE_VECTOR_MATH_ABSOLUTE,
NODE_VECTOR_MATH_MINIMUM,
NODE_VECTOR_MATH_MAXIMUM,
NODE_VECTOR_MATH_WRAP,
NODE_VECTOR_MATH_SINE,
NODE_VECTOR_MATH_COSINE,
NODE_VECTOR_MATH_TANGENT,
NODE_VECTOR_MATH_REFRACT,
NODE_VECTOR_MATH_FACEFORWARD,
NODE_VECTOR_MATH_MULTIPLY_ADD,
NODE_VECTOR_MATH_POWER,
NODE_VECTOR_MATH_SIGN,
NODE_VECTOR_MATH_ROUND,
};
enum NodeClampType : uint {
NODE_CLAMP_MINMAX,
NODE_CLAMP_RANGE,
};
enum NodeMapRangeType : uint {
NODE_MAP_RANGE_LINEAR,
NODE_MAP_RANGE_STEPPED,
NODE_MAP_RANGE_SMOOTHSTEP,
NODE_MAP_RANGE_SMOOTHERSTEP,
};
enum NodeMappingType : uint {
NODE_MAPPING_TYPE_POINT,
NODE_MAPPING_TYPE_TEXTURE,
NODE_MAPPING_TYPE_VECTOR,
NODE_MAPPING_TYPE_NORMAL
};
enum NodeVectorRotateType : uint {
NODE_VECTOR_ROTATE_TYPE_AXIS,
NODE_VECTOR_ROTATE_TYPE_AXIS_X,
NODE_VECTOR_ROTATE_TYPE_AXIS_Y,
NODE_VECTOR_ROTATE_TYPE_AXIS_Z,
NODE_VECTOR_ROTATE_TYPE_EULER_XYZ,
};
enum NodeVectorTransformType : uint {
NODE_VECTOR_TRANSFORM_TYPE_VECTOR,
NODE_VECTOR_TRANSFORM_TYPE_POINT,
NODE_VECTOR_TRANSFORM_TYPE_NORMAL
};
enum NodeVectorTransformConvertSpace : uint {
NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD,
NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT,
NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA
};
enum NodeConvert : uint {
NODE_CONVERT_FV,
NODE_CONVERT_FI,
NODE_CONVERT_CF,
NODE_CONVERT_CI,
NODE_CONVERT_VF,
NODE_CONVERT_VI,
NODE_CONVERT_IF,
NODE_CONVERT_IV,
NODE_CONVERT_NONE,
};
enum NodeNoiseType : uint {
NODE_NOISE_MULTIFRACTAL,
NODE_NOISE_FBM,
NODE_NOISE_HYBRID_MULTIFRACTAL,
NODE_NOISE_RIDGED_MULTIFRACTAL,
NODE_NOISE_HETERO_TERRAIN
};
enum NodeGaborType : uint {
NODE_GABOR_TYPE_2D,
NODE_GABOR_TYPE_3D,
};
enum NodeWaveType : uint { NODE_WAVE_BANDS, NODE_WAVE_RINGS };
enum NodeWaveBandsDirection : uint {
NODE_WAVE_BANDS_DIRECTION_X,
NODE_WAVE_BANDS_DIRECTION_Y,
NODE_WAVE_BANDS_DIRECTION_Z,
NODE_WAVE_BANDS_DIRECTION_DIAGONAL
};
enum NodeWaveRingsDirection : uint {
NODE_WAVE_RINGS_DIRECTION_X,
NODE_WAVE_RINGS_DIRECTION_Y,
NODE_WAVE_RINGS_DIRECTION_Z,
NODE_WAVE_RINGS_DIRECTION_SPHERICAL
};
enum NodeWaveProfile : uint {
NODE_WAVE_PROFILE_SIN,
NODE_WAVE_PROFILE_SAW,
NODE_WAVE_PROFILE_TRI,
};
enum NodeSkyType : uint {
NODE_SKY_PREETHAM,
NODE_SKY_HOSEK,
NODE_SKY_SINGLE_SCATTERING,
NODE_SKY_MULTIPLE_SCATTERING
};
enum NodeGradientType : uint {
NODE_BLEND_LINEAR,
NODE_BLEND_QUADRATIC,
NODE_BLEND_EASING,
NODE_BLEND_DIAGONAL,
NODE_BLEND_RADIAL,
NODE_BLEND_QUADRATIC_SPHERE,
NODE_BLEND_SPHERICAL
};
enum NodeVoronoiDistanceMetric : uint {
NODE_VORONOI_EUCLIDEAN,
NODE_VORONOI_MANHATTAN,
NODE_VORONOI_CHEBYCHEV,
NODE_VORONOI_MINKOWSKI,
};
enum NodeVoronoiFeature : uint {
NODE_VORONOI_F1,
NODE_VORONOI_F2,
NODE_VORONOI_SMOOTH_F1,
NODE_VORONOI_DISTANCE_TO_EDGE,
NODE_VORONOI_N_SPHERE_RADIUS,
};
enum NodeBlendWeightType : uint { NODE_LAYER_WEIGHT_FRESNEL, NODE_LAYER_WEIGHT_FACING };
enum NodeTangentDirectionType : uint { NODE_TANGENT_RADIAL, NODE_TANGENT_UVMAP };
enum NodeTangentAxis : uint { NODE_TANGENT_AXIS_X, NODE_TANGENT_AXIS_Y, NODE_TANGENT_AXIS_Z };
enum NodeNormalMapSpace : uint {
NODE_NORMAL_MAP_TANGENT,
NODE_NORMAL_MAP_OBJECT,
NODE_NORMAL_MAP_WORLD,
NODE_NORMAL_MAP_BLENDER_OBJECT,
NODE_NORMAL_MAP_BLENDER_WORLD,
};
enum NodeNormalMapConvention {
NODE_NORMAL_MAP_CONVENTION_OPENGL = 0,
NODE_NORMAL_MAP_CONVENTION_DIRECTX = 1,
};
enum NodeNormalMapBase {
NODE_NORMAL_MAP_BASE_ORIGINAL = 0,
NODE_NORMAL_MAP_BASE_DISPLACED = 1,
};
/* Flags for SVM node encoding, packing space/convention/base into one byte. */
enum NodeNormalMapFlags {
NODE_NORMAL_MAP_FLAG_SPACE_MASK = 0x7,
NODE_NORMAL_MAP_FLAG_DIRECTX = (1 << 3),
NODE_NORMAL_MAP_FLAG_ORIGINAL = (1 << 4),
};
enum NodeImageProjection : uint {
NODE_IMAGE_PROJ_FLAT = 0,
NODE_IMAGE_PROJ_BOX = 1,
NODE_IMAGE_PROJ_SPHERE = 2,
NODE_IMAGE_PROJ_TUBE = 3,
};
enum NodeImageFlags {
NODE_IMAGE_COMPRESS_AS_SRGB = 1,
NODE_IMAGE_ALPHA_UNASSOCIATE = 2,
};
enum NodeEnvironmentProjection : uint {
NODE_ENVIRONMENT_EQUIRECTANGULAR = 0,
NODE_ENVIRONMENT_MIRROR_BALL = 1,
};
enum NodeBumpOffset : uint8_t {
NODE_BUMP_OFFSET_CENTER,
NODE_BUMP_OFFSET_DX,
NODE_BUMP_OFFSET_DY,
};
enum NodeAO {
NODE_AO_ONLY_LOCAL = (1 << 0),
NODE_AO_INSIDE = (1 << 1),
NODE_AO_GLOBAL_RADIUS = (1 << 2),
};
enum ShaderType {
SHADER_TYPE_SURFACE,
SHADER_TYPE_VOLUME,
SHADER_TYPE_DISPLACEMENT,
SHADER_TYPE_BUMP,
};
enum NodePrincipledHairModel : uint {
NODE_PRINCIPLED_HAIR_CHIANG = 0,
NODE_PRINCIPLED_HAIR_HUANG = 1,
NODE_PRINCIPLED_HAIR_MODEL_NUM,
};
enum NodePrincipledHairParametrization : uint {
NODE_PRINCIPLED_HAIR_REFLECTANCE = 0,
NODE_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION = 1,
NODE_PRINCIPLED_HAIR_DIRECT_ABSORPTION = 2,
NODE_PRINCIPLED_HAIR_PARAMETRIZATION_NUM,
};
enum NodeCombSepColorType : uint {
NODE_COMBSEP_COLOR_RGB,
NODE_COMBSEP_COLOR_HSV,
NODE_COMBSEP_COLOR_HSL,
};
/* Closure */
enum ClosureType : uint {
/* Special type, flags generic node as a non-BSDF. */
CLOSURE_NONE_ID,
CLOSURE_BSDF_ID,
/* Diffuse */
CLOSURE_BSDF_DIFFUSE_ID,
CLOSURE_BSDF_OREN_NAYAR_ID,
CLOSURE_BSDF_ROUGH_TRANSLUCENT_ID,
CLOSURE_BSDF_BURLEY_ID,
CLOSURE_BSDF_DIFFUSE_RAMP_ID,
CLOSURE_BSDF_SHEEN_ID,
CLOSURE_BSDF_DIFFUSE_TOON_ID,
CLOSURE_BSDF_TRANSLUCENT_ID,
/* Glossy */
CLOSURE_BSDF_PHYSICAL_CONDUCTOR, /* virtual closure */
CLOSURE_BSDF_F82_CONDUCTOR, /* virtual closure */
CLOSURE_BSDF_MICROFACET_GGX_ID,
CLOSURE_BSDF_MICROFACET_BECKMANN_ID,
CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID, /* virtual closure */
CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID,
CLOSURE_BSDF_ASHIKHMIN_VELVET_ID,
CLOSURE_BSDF_PHONG_RAMP_ID,
CLOSURE_BSDF_GLOSSY_TOON_ID,
CLOSURE_BSDF_HAIR_REFLECTION_ID,
/* Transmission */
CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID,
CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID,
CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID,
CLOSURE_BSDF_HAIR_TRANSMISSION_ID,
/* Glass */
CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID, /* virtual closure */
CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID, /* virtual closure */
CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID, /* virtual closure */
CLOSURE_BSDF_HAIR_CHIANG_ID,
CLOSURE_BSDF_HAIR_HUANG_ID,
/* Special cases */
CLOSURE_BSDF_RAY_PORTAL_ID,
CLOSURE_BSDF_TRANSPARENT_ID,
/* BSSRDF */
CLOSURE_BSSRDF_BURLEY_ID,
CLOSURE_BSSRDF_RANDOM_WALK_ID,
CLOSURE_BSSRDF_RANDOM_WALK_LEGACY_ID,
CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID,
/* Other */
CLOSURE_HOLDOUT_ID,
/* Volume */
CLOSURE_VOLUME_ID,
CLOSURE_VOLUME_ABSORPTION_ID,
CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID,
CLOSURE_VOLUME_MIE_ID, /* virtual closure */
CLOSURE_VOLUME_FOURNIER_FORAND_ID,
CLOSURE_VOLUME_RAYLEIGH_ID,
CLOSURE_VOLUME_DRAINE_ID,
CLOSURE_BSDF_PRINCIPLED_ID,
NBUILTIN_CLOSURES
};
static_assert(NBUILTIN_CLOSURES < 256, "Too many Closure types (need to change SVM packing)");
/* watch this, being lazy with memory usage */
#define CLOSURE_IS_BSDF(type) (type != CLOSURE_NONE_ID && type <= CLOSURE_BSDF_TRANSPARENT_ID)
#define CLOSURE_IS_BSDF_DIFFUSE(type) \
(type >= CLOSURE_BSDF_DIFFUSE_ID && type <= CLOSURE_BSDF_TRANSLUCENT_ID)
#define CLOSURE_IS_BSDF_GLOSSY(type) \
((type >= CLOSURE_BSDF_MICROFACET_GGX_ID && type <= CLOSURE_BSDF_HAIR_REFLECTION_ID) || \
(type == CLOSURE_BSDF_HAIR_CHIANG_ID) || (type == CLOSURE_BSDF_HAIR_HUANG_ID))
#define CLOSURE_IS_BSDF_TRANSMISSION(type) \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID && \
type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID)
#define CLOSURE_IS_BSDF_SINGULAR(type) \
(type == CLOSURE_BSDF_TRANSPARENT_ID || type == CLOSURE_BSDF_RAY_PORTAL_ID)
#define CLOSURE_IS_BSDF_TRANSPARENT(type) (type == CLOSURE_BSDF_TRANSPARENT_ID)
#define CLOSURE_IS_BSDF_MULTISCATTER(type) \
(type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID || \
type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_BSDF_MICROFACET(type) \
((type >= CLOSURE_BSDF_MICROFACET_GGX_ID && type <= CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID) || \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID && \
type <= CLOSURE_BSDF_THIN_GLASS_TRANSMISSION_ID) || \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID && \
type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID))
#define CLOSURE_IS_BSDF_OR_BSSRDF(type) \
(type != CLOSURE_NONE_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID)
#define CLOSURE_IS_BSSRDF(type) \
(type >= CLOSURE_BSSRDF_BURLEY_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_SKIN_ID)
#define CLOSURE_IS_VOLUME(type) (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_DRAINE_ID)
#define CLOSURE_IS_VOLUME_SCATTER(type) \
(type >= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID && type <= CLOSURE_VOLUME_DRAINE_ID)
#define CLOSURE_IS_VOLUME_ABSORPTION(type) (type == CLOSURE_VOLUME_ABSORPTION_ID)
#define CLOSURE_IS_HOLDOUT(type) (type == CLOSURE_HOLDOUT_ID)
#define CLOSURE_IS_PHASE(type) \
(type >= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID && type <= CLOSURE_VOLUME_DRAINE_ID)
#define CLOSURE_IS_REFRACTION(type) \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID && \
type <= CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID)
#define CLOSURE_IS_GLASS(type) \
(type >= CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID && \
type <= CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID)
#define CLOSURE_IS_PRINCIPLED(type) (type == CLOSURE_BSDF_PRINCIPLED_ID)
#define CLOSURE_IS_RAY_PORTAL(type) (type == CLOSURE_BSDF_RAY_PORTAL_ID)
#define CLOSURE_WEIGHT_CUTOFF 1e-5f
#define THINFILM_THICKNESS_CUTOFF 0.1f
CCL_NAMESPACE_END

View File

@@ -0,0 +1,269 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/types.h"
#include "kernel/svm/types.h"
CCL_NAMESPACE_BEGIN
/* Stack Load */
ccl_device_inline float stack_load_float(const ccl_private float *stack, const uint a)
{
kernel_assert(a < SVM_STACK_SIZE);
return stack[a];
}
ccl_device_inline float stack_load_float_default(const ccl_private float *stack,
const uint a,
const float value)
{
return (a == (uint)SVM_STACK_INVALID) ? value : stack_load_float(stack, a);
}
ccl_device_inline float3 stack_load_float3(const ccl_private float *stack, const uint a)
{
kernel_assert(a + 2 < SVM_STACK_SIZE);
const ccl_private float *stack_a = stack + a;
return make_float3(stack_a[0], stack_a[1], stack_a[2]);
}
ccl_device_inline float3 stack_load_float3_default(const ccl_private float *stack,
const uint a,
const float3 value)
{
return (a == (uint)SVM_STACK_INVALID) ? value : stack_load_float3(stack, a);
}
ccl_device_inline int stack_load_int(const ccl_private float *stack, const uint a)
{
kernel_assert(a < SVM_STACK_SIZE);
return __float_as_int(stack[a]);
}
/* Type-based stack load. T can be float, float3, dual1, or dual3.
* When T is a dual type, derivatives are loaded from adjacent stack slots. */
template<typename T> ccl_device_inline T stack_load(const ccl_private float *stack, const uint a);
ccl_device_template_spec float stack_load(const ccl_private float *stack, const uint a)
{
return stack_load_float(stack, a);
}
ccl_device_template_spec float3 stack_load(const ccl_private float *stack, const uint a)
{
return stack_load_float3(stack, a);
}
ccl_device_template_spec dual1 stack_load(const ccl_private float *stack, const uint a)
{
return {
stack_load_float(stack, a), stack_load_float(stack, a + 1), stack_load_float(stack, a + 2)};
}
ccl_device_template_spec dual3 stack_load(const ccl_private float *stack, const uint a)
{
return {stack_load_float3(stack, a),
stack_load_float3(stack, a + 3),
stack_load_float3(stack, a + 6)};
}
/* Load from SVMInputFloat and SVMInputFloat3. With template versions to support duals
* for loading derivatives from adjacent stack slots. */
ccl_device_inline float stack_load(const ccl_private float *ccl_restrict stack,
const SVMInputFloat v)
{
if ((v.bits >> 8) == (SVM_INPUT_STACK_OFFSET_MASK >> 8)) {
return stack_load_float(stack, v.bits & 0xFFu);
}
return __uint_as_float(v.bits);
}
ccl_device_inline float3 stack_load(const ccl_private float *ccl_restrict stack,
const SVMInputFloat3 v)
{
if ((v.x.bits >> 8) == (SVM_INPUT_STACK_OFFSET_MASK >> 8)) {
return stack_load_float3(stack, v.x.bits & 0xFFu);
}
return make_float3(
__uint_as_float(v.x.bits), __uint_as_float(v.y.bits), __uint_as_float(v.z.bits));
}
ccl_device_inline int stack_load(const ccl_private float *stack, const SVMInputInt v)
{
if (v.offset == SVM_STACK_INVALID) {
return v.value;
}
return stack_load_int(stack, v.offset);
}
template<typename T>
ccl_device_inline T stack_load(const ccl_private float *stack, const SVMInputFloat v);
ccl_device_template_spec float stack_load(const ccl_private float *stack, const SVMInputFloat v)
{
return stack_load(stack, v);
}
ccl_device_template_spec dual1 stack_load(const ccl_private float *stack, const SVMInputFloat v)
{
if ((v.bits >> 8) == (SVM_INPUT_STACK_OFFSET_MASK >> 8)) {
return stack_load<dual1>(stack, v.bits & 0xFFu);
}
return dual1(__uint_as_float(v.bits));
}
template<typename T>
ccl_device_inline T stack_load(const ccl_private float *stack, const SVMInputFloat3 v);
ccl_device_template_spec float3 stack_load(const ccl_private float *stack, const SVMInputFloat3 v)
{
return stack_load(stack, v);
}
ccl_device_template_spec dual3 stack_load(const ccl_private float *stack, const SVMInputFloat3 v)
{
if ((v.x.bits >> 8) == (SVM_INPUT_STACK_OFFSET_MASK >> 8)) {
return stack_load<dual3>(stack, v.x.bits & 0xFFu);
}
return dual3(make_float3(
__uint_as_float(v.x.bits), __uint_as_float(v.y.bits), __uint_as_float(v.z.bits)));
}
/* Stack Store */
ccl_device_inline void stack_store_float(ccl_private float *stack, const uint a, const float f)
{
kernel_assert(a < SVM_STACK_SIZE);
stack[a] = f;
}
ccl_device_inline void stack_store_float3(ccl_private float *stack, const uint a, const float3 f)
{
kernel_assert(a + 2 < SVM_STACK_SIZE);
copy_v3_v3(stack + a, f);
}
ccl_device_inline void stack_store_int(ccl_private float *stack, const uint a, const int i)
{
kernel_assert(a < SVM_STACK_SIZE);
stack[a] = __int_as_float(i);
}
/* Type-based stack store. Overloaded for plain and dual types.
* For dual types, derivatives are stored in adjacent stack slots. */
ccl_device_inline void stack_store(ccl_private float *stack, const uint a, const float f)
{
stack_store_float(stack, a, f);
}
ccl_device_inline void stack_store(ccl_private float *stack, const uint a, const float3 f)
{
stack_store_float3(stack, a, f);
}
ccl_device_inline void stack_store(ccl_private float *stack, const uint a, const dual1 f)
{
stack_store_float(stack, a, f.val);
stack_store_float(stack, a + 1, f.dx);
stack_store_float(stack, a + 2, f.dy);
}
ccl_device_inline void stack_store(ccl_private float *stack, const uint a, const dual3 f)
{
stack_store_float3(stack, a, f.val);
stack_store_float3(stack, a + 3, f.dx);
stack_store_float3(stack, a + 6, f.dy);
}
/* Stack Utility */
ccl_device_inline bool stack_valid(const uint a)
{
return a != (uint)SVM_STACK_INVALID;
}
/* Reading Nodes */
/* Read a typed node struct directly from the SVM byte-code stream. The struct T must be a
* multiple of sizeof(uint) and its memory layout must match the byte-code encoding. Returns
* a const reference into the byte-code array and advances the offset past the struct. */
template<typename T>
ccl_device_inline const ccl_global T &svm_node_get(KernelGlobals kg, ccl_private int *const offset)
{
static_assert(alignof(T) <= alignof(uint));
static_assert(sizeof(T) % sizeof(uint) == 0);
const ccl_global T &node = *reinterpret_cast<const ccl_global T *>(
&kernel_data_fetch(svm_nodes, *offset));
*offset += sizeof(T) / sizeof(uint);
return node;
}
ccl_device_inline float4 svm_node_get_data_float4(KernelGlobals kg, const int offset)
{
return make_float4(__uint_as_float(kernel_data_fetch(svm_nodes, offset)),
__uint_as_float(kernel_data_fetch(svm_nodes, offset + 1)),
__uint_as_float(kernel_data_fetch(svm_nodes, offset + 2)),
__uint_as_float(kernel_data_fetch(svm_nodes, offset + 3)));
}
/* Shading Helpers */
ccl_device_forceinline float3 dPdx(const ccl_private ShaderData *sd)
{
return sd->dPdu * sd->du.dx + sd->dPdv * sd->dv.dx;
}
ccl_device_forceinline float3 dPdy(const ccl_private ShaderData *sd)
{
return sd->dPdu * sd->du.dy + sd->dPdv * sd->dv.dy;
}
/* Shading position, returns Float3Type = float3 (no derivatives) or dual3 (with derivatives). */
template<typename Float3Type>
ccl_device_inline Float3Type shading_position(const ccl_private ShaderData *sd)
{
if constexpr (is_dual_v<Float3Type>) {
dual3 P(sd->P);
P.dx = dPdx(sd);
P.dy = dPdy(sd);
return P;
}
else {
return sd->P;
}
}
/* Shading incoming direction, returns Float3Type = float3 or dual3. */
template<typename Float3Type>
ccl_device_inline Float3Type shading_incoming(const ccl_private ShaderData *sd)
{
if constexpr (is_dual_v<Float3Type>) {
dual3 I(sd->wi);
float3 dIdx, dIdy;
make_orthonormals(sd->wi, &dIdx, &dIdy);
I.dx = sd->dI * dIdx;
I.dy = sd->dI * dIdy;
return I;
}
else {
return sd->wi;
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,30 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Value Nodes */
template<typename FloatType>
ccl_device void svm_node_value_f(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeValueF &ccl_restrict node)
{
/* Derivative of a constant is zero. */
stack_store(stack, node.out_offset, FloatType(node.value));
}
template<typename Float3Type>
ccl_device void svm_node_value_v(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeValueV &ccl_restrict node)
{
/* Derivative of a constant is zero. */
stack_store(stack, node.out_offset, Float3Type(node.value));
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Vector Rotate */
ccl_device_noinline void svm_node_vector_rotate(
ccl_private float *ccl_restrict stack, const ccl_global SVMNodeVectorRotate &ccl_restrict node)
{
if (stack_valid(node.result_offset)) {
const float3 vector = stack_load(stack, node.vector);
const float3 center = stack_load(stack, node.center);
float3 result = make_float3(0.0f, 0.0f, 0.0f);
if (node.rotate_type == NODE_VECTOR_ROTATE_TYPE_EULER_XYZ) {
const float3 rotation = stack_load(stack, node.rotation); // Default XYZ.
const Transform rotationTransform = euler_to_transform(rotation);
if (node.invert) {
result = transform_direction_transposed(&rotationTransform, vector - center) + center;
}
else {
result = transform_direction(&rotationTransform, vector - center) + center;
}
}
else {
float3 axis;
float axis_length;
switch (node.rotate_type) {
case NODE_VECTOR_ROTATE_TYPE_AXIS_X:
axis = make_float3(1.0f, 0.0f, 0.0f);
axis_length = 1.0f;
break;
case NODE_VECTOR_ROTATE_TYPE_AXIS_Y:
axis = make_float3(0.0f, 1.0f, 0.0f);
axis_length = 1.0f;
break;
case NODE_VECTOR_ROTATE_TYPE_AXIS_Z:
axis = make_float3(0.0f, 0.0f, 1.0f);
axis_length = 1.0f;
break;
default:
axis = stack_load(stack, node.axis);
axis_length = len(axis);
break;
}
float angle = stack_load(stack, node.angle);
angle = node.invert ? -angle : angle;
result = (axis_length != 0.0f) ?
rotate_around_axis(vector - center, axis / axis_length, angle) + center :
vector;
}
stack_store_float3(stack, node.result_offset, result);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,123 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/geom/object.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Vector Transform */
ccl_device_noinline void svm_node_vector_transform(
KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeVectorTransform &ccl_restrict node)
{
float3 in = stack_load(stack, node.vector_in);
const NodeVectorTransformType type = node.transform_type;
const NodeVectorTransformConvertSpace from = node.convert_from;
const NodeVectorTransformConvertSpace to = node.convert_to;
Transform tfm;
const bool is_object = (sd->object != OBJECT_NONE);
const bool is_normal = (type == NODE_VECTOR_TRANSFORM_TYPE_NORMAL);
const bool is_direction = (type == NODE_VECTOR_TRANSFORM_TYPE_VECTOR);
/* From world */
if (from == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD) {
if (to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA) {
if (is_normal) {
tfm = kernel_data.cam.cameratoworld;
in = normalize(transform_direction_transposed(&tfm, in));
}
else {
tfm = kernel_data.cam.worldtocamera;
in = is_direction ? transform_direction(&tfm, in) : transform_point(&tfm, in);
}
}
else if (to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT && is_object) {
if (is_normal) {
object_inverse_normal_transform(kg, sd, &in);
}
else if (is_direction) {
object_inverse_dir_transform(kg, sd, &in);
}
else {
object_inverse_position_transform(kg, sd, &in);
}
}
}
/* From camera */
else if (from == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA) {
if (to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD ||
to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT)
{
if (is_normal) {
tfm = kernel_data.cam.worldtocamera;
in = normalize(transform_direction_transposed(&tfm, in));
}
else {
tfm = kernel_data.cam.cameratoworld;
in = is_direction ? transform_direction(&tfm, in) : transform_point(&tfm, in);
}
}
if (to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT && is_object) {
if (is_normal) {
object_inverse_normal_transform(kg, sd, &in);
}
else if (is_direction) {
object_inverse_dir_transform(kg, sd, &in);
}
else {
object_inverse_position_transform(kg, sd, &in);
}
}
}
/* From object */
else if (from == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT) {
if ((to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD ||
to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA) &&
is_object)
{
if (is_normal) {
object_normal_transform(kg, sd, &in);
}
else if (is_direction) {
object_dir_transform(kg, sd, &in);
}
else {
object_position_transform(kg, sd, &in);
}
}
if (to == NODE_VECTOR_TRANSFORM_CONVERT_SPACE_CAMERA) {
if (is_normal) {
tfm = kernel_data.cam.cameratoworld;
in = normalize(transform_direction_transposed(&tfm, in));
}
else {
tfm = kernel_data.cam.worldtocamera;
if (is_direction) {
in = transform_direction(&tfm, in);
}
else {
in = transform_point(&tfm, in);
}
}
}
}
/* Output */
if (stack_valid(node.vector_out_offset)) {
stack_store_float3(stack, node.vector_out_offset, in);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,96 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/geom/attribute.h"
#include "kernel/geom/primitive.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "util/math_base.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_vertex_color(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeVertexColor &ccl_restrict
node)
{
float3 color;
float alpha;
const AttributeDescriptor descriptor = find_attribute(kg, sd, node.layer_id);
if (is_attribute_found(descriptor)) {
if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) {
const float4 vertex_color = primitive_surface_attribute<float4>(kg, sd, descriptor);
color = make_float3(vertex_color);
alpha = vertex_color.w;
}
else {
color = primitive_surface_attribute<float3>(kg, sd, descriptor);
alpha = 1.0f;
}
}
else {
color = make_float3(0.0f, 0.0f, 0.0f);
alpha = 0.0f;
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, color);
}
if (stack_valid(node.alpha_offset)) {
stack_store_float(stack, node.alpha_offset, alpha);
}
}
ccl_device_noinline void svm_node_vertex_color_derivative(
KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeVertexColor &ccl_restrict node)
{
float3 color;
float alpha;
const AttributeDescriptor descriptor = find_attribute(kg, sd, node.layer_id);
if (is_attribute_found(descriptor)) {
if (descriptor.type == NODE_ATTR_FLOAT4 || descriptor.type == NODE_ATTR_RGBA) {
dual4 vertex_color = primitive_surface_attribute<dual4>(kg, sd, descriptor);
if (node.bump_offset == NODE_BUMP_OFFSET_DX) {
vertex_color.val += vertex_color.dx * node.bump_filter_width;
}
else if (node.bump_offset == NODE_BUMP_OFFSET_DY) {
vertex_color.val += vertex_color.dy * node.bump_filter_width;
}
color = make_float3(vertex_color.val);
alpha = vertex_color.val.w;
}
else {
dual3 vertex_color = primitive_surface_attribute<dual3>(kg, sd, descriptor);
if (node.bump_offset == NODE_BUMP_OFFSET_DX) {
vertex_color.val += vertex_color.dx * node.bump_filter_width;
}
else if (node.bump_offset == NODE_BUMP_OFFSET_DY) {
vertex_color.val += vertex_color.dy * node.bump_filter_width;
}
color = vertex_color.val;
alpha = 1.0f;
}
}
else {
color = make_float3(0.0f, 0.0f, 0.0f);
alpha = 0.0f;
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, color);
}
if (stack_valid(node.alpha_offset)) {
stack_store_float(stack, node.alpha_offset, alpha);
}
}
CCL_NAMESPACE_END

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,109 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/fractal_noise.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
CCL_NAMESPACE_BEGIN
/* Wave */
ccl_device_noinline_cpu float svm_wave(NodeWaveType type,
NodeWaveBandsDirection bands_dir,
NodeWaveRingsDirection rings_dir,
NodeWaveProfile profile,
float3 p,
const float distortion,
const float detail,
const float dscale,
const float droughness,
const float phase)
{
/* Prevent precision issues on unit coordinates. */
p = (p + 0.000001f) * 0.999999f;
float n;
if (type == NODE_WAVE_BANDS) {
if (bands_dir == NODE_WAVE_BANDS_DIRECTION_X) {
n = p.x * 20.0f;
}
else if (bands_dir == NODE_WAVE_BANDS_DIRECTION_Y) {
n = p.y * 20.0f;
}
else if (bands_dir == NODE_WAVE_BANDS_DIRECTION_Z) {
n = p.z * 20.0f;
}
else { /* NODE_WAVE_BANDS_DIRECTION_DIAGONAL */
n = (p.x + p.y + p.z) * 10.0f;
}
}
else { /* NODE_WAVE_RINGS */
float3 rp = p;
if (rings_dir == NODE_WAVE_RINGS_DIRECTION_X) {
rp *= make_float3(0.0f, 1.0f, 1.0f);
}
else if (rings_dir == NODE_WAVE_RINGS_DIRECTION_Y) {
rp *= make_float3(1.0f, 0.0f, 1.0f);
}
else if (rings_dir == NODE_WAVE_RINGS_DIRECTION_Z) {
rp *= make_float3(1.0f, 1.0f, 0.0f);
}
/* else: NODE_WAVE_RINGS_DIRECTION_SPHERICAL */
n = len(rp) * 20.0f;
}
n += phase;
if (distortion != 0.0f) {
n += distortion * (noise_fbm(p * dscale, detail, droughness, 2.0f, true) * 2.0f - 1.0f);
}
if (profile == NODE_WAVE_PROFILE_SIN) {
return 0.5f + 0.5f * sinf(n - M_PI_2_F);
}
if (profile == NODE_WAVE_PROFILE_SAW) {
n /= M_2PI_F;
return n - floorf(n);
}
/* NODE_WAVE_PROFILE_TRI */
n /= M_2PI_F;
return fabsf(n - floorf(n + 0.5f)) * 2.0f;
}
ccl_device_noinline void svm_node_tex_wave(ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexWave &ccl_restrict node)
{
const float3 co = stack_load_float3(stack, node.co);
const float scale = stack_load(stack, node.scale);
const float distortion = stack_load(stack, node.distortion);
const float detail = stack_load(stack, node.detail);
const float dscale = stack_load(stack, node.dscale);
const float droughness = stack_load(stack, node.droughness);
const float phase = stack_load(stack, node.phase);
const float f = svm_wave(node.wave_type,
node.bands_direction,
node.rings_direction,
node.profile,
co * scale,
distortion,
detail,
dscale,
droughness,
phase);
if (stack_valid(node.fac_offset)) {
stack_store_float(stack, node.fac_offset, f);
}
if (stack_valid(node.color_offset)) {
stack_store_float3(stack, node.color_offset, make_float3(f, f, f));
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,36 @@
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Adapted code from Open Shading Language. */
#pragma once
#include "kernel/svm/math_util.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/colorspace.h"
CCL_NAMESPACE_BEGIN
/* Wavelength to RGB */
ccl_device_noinline void svm_node_wavelength(KernelGlobals kg,
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeWavelength &ccl_restrict node)
{
const float lambda_nm = stack_load(stack, node.wavelength);
float3 color = svm_math_wavelength_color_xyz(lambda_nm);
color = xyz_to_rgb(kg, color);
color *= 1.0f / 2.52f; // Empirical scale from lg to make all comps <= 1
/* Clamp to zero if values are smaller */
color = max(color, make_float3(0.0f, 0.0f, 0.0f));
stack_store_float3(stack, node.color_offset, color);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,67 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "util/hash.h"
CCL_NAMESPACE_BEGIN
ccl_device_noinline void svm_node_tex_white_noise(
ccl_private float *ccl_restrict stack,
const ccl_global SVMNodeTexWhiteNoise &ccl_restrict node)
{
const float3 vector = stack_load(stack, node.vector);
const float w = stack_load(stack, node.w);
if (stack_valid(node.color_offset)) {
float3 color;
switch (node.dimensions) {
case 1:
color = hash_float_to_float3(w);
break;
case 2:
color = hash_float2_to_float3(make_float2(vector.x, vector.y));
break;
case 3:
color = hash_float3_to_float3(vector);
break;
case 4:
color = hash_float4_to_float3(make_float4(vector, w));
break;
default:
color = make_float3(1.0f, 0.0f, 1.0f);
kernel_assert(0);
break;
}
stack_store_float3(stack, node.color_offset, color);
}
if (stack_valid(node.value_offset)) {
float value;
switch (node.dimensions) {
case 1:
value = hash_float_to_float(w);
break;
case 2:
value = hash_float2_to_float(make_float2(vector.x, vector.y));
break;
case 3:
value = hash_float3_to_float(vector);
break;
case 4:
value = hash_float4_to_float(make_float4(vector, w));
break;
default:
value = 0.0f;
kernel_assert(0);
break;
}
stack_store_float(stack, node.value_offset, value);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,110 @@
/* SPDX-FileCopyrightText: 2009-2010 Sony Pictures Imageworks Inc., et al. All Rights Reserved.
* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: BSD-3-Clause
*
* Adapted code from Open Shading Language. */
#pragma once
#include "kernel/geom/motion_triangle.h"
#include "kernel/geom/object.h"
#include "kernel/geom/triangle.h"
#include "kernel/svm/node_types.h"
#include "kernel/svm/util.h"
#include "kernel/util/differential.h"
#include "util/math_base.h"
CCL_NAMESPACE_BEGIN
/* Wireframe Node */
ccl_device_inline float wireframe(KernelGlobals kg,
ccl_private ShaderData *sd,
const differential3 dP,
const float size,
const int pixel_size,
ccl_private float3 *P)
{
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (sd->prim != PRIM_NONE && sd->type & PRIMITIVE_TRIANGLE)
#else
if (sd->prim != PRIM_NONE)
#endif
{
float3 Co[3];
float pixelwidth = 1.0f;
/* Triangles */
const int np = 3;
if (sd->type & PRIMITIVE_MOTION) {
motion_triangle_vertices(kg, sd->object, sd->prim, sd->time, Co);
}
else {
triangle_vertices(kg, sd->object, sd->prim, Co);
}
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &Co[0]);
object_position_transform(kg, sd, &Co[1]);
object_position_transform(kg, sd, &Co[2]);
}
if (pixel_size) {
// Project the derivatives of P to the viewing plane defined
// by I so we have a measure of how big is a pixel at this point
const float pixelwidth_x = len(dP.dx - dot(dP.dx, sd->wi) * sd->wi);
const float pixelwidth_y = len(dP.dy - dot(dP.dy, sd->wi) * sd->wi);
// Take the average of both axis' length
pixelwidth = (pixelwidth_x + pixelwidth_y) * 0.5f;
}
// Use half the width as the neighbor face will render the
// other half. And take the square for fast comparison
pixelwidth *= 0.5f * size;
pixelwidth *= pixelwidth;
for (int i = 0; i < np; i++) {
const int i2 = i ? i - 1 : np - 1;
const float3 dir = *P - Co[i];
const float3 edge = Co[i] - Co[i2];
const float3 crs = cross(edge, dir);
// At this point dot(crs, crs) / dot(edge, edge) is
// the square of area / length(edge) == square of the
// distance to the edge.
if (dot(crs, crs) < (dot(edge, edge) * pixelwidth)) {
return 1.0f;
}
}
}
return 0.0f;
}
ccl_device_noinline void svm_node_wireframe(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float *stack,
const ccl_global SVMNodeWireframe &ccl_restrict node)
{
/* Input Data */
const float size = stack_load(stack, node.in_size);
const int pixel_size = (int)node.use_pixel_size;
/* Calculate wireframe */
const differential3 dP = differential_from_compact(sd->Ng, sd->dP);
float3 P = sd->P;
if (node.bump_offset == NODE_BUMP_OFFSET_DX) {
P += dP.dx * node.bump_filter_width;
}
else if (node.bump_offset == NODE_BUMP_OFFSET_DY) {
P += dP.dy * node.bump_filter_width;
}
const float f = wireframe(kg, sd, dP, size, pixel_size, &P);
if (stack_valid(node.out_fac_offset)) {
stack_store_float(stack, node.out_fac_offset, f);
}
}
CCL_NAMESPACE_END