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,264 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/types.h"
#include "kernel/util/colorspace.h"
#include "util/color.h"
CCL_NAMESPACE_BEGIN
/* Attributes
*
* We support an arbitrary number of attributes on various mesh elements.
* On vertices, triangles, curve keys, curves, meshes and volume grids.
* Most of the code for attribute reading is in the primitive files.
*
* Lookup of attributes is different between OSL and SVM, as OSL is ustring
* based while for SVM we use integer ids. */
ccl_device_forceinline bool is_attribute_found(const ccl_private AttributeDescriptor &desc)
{
return desc.offset != ATTR_STD_NOT_FOUND;
}
ccl_device_inline AttributeDescriptor attribute_not_found()
{
const AttributeDescriptor desc = {ATTR_ELEMENT_NONE, (NodeAttributeType)0, ATTR_STD_NOT_FOUND};
return desc;
}
/* Find attribute based on ID */
ccl_device_inline uint object_attribute_map_offset(KernelGlobals kg, const int object)
{
return kernel_data_fetch(objects, object).attribute_map_offset;
}
ccl_device bool find_attr_offset(const ccl_global AttributeMap *attributes_map,
ccl_private uint &attr_offset,
const uint64_t id)
{
/* For SVM, find attribute by unique id. */
AttributeMap attr_map = attributes_map[attr_offset];
while (attr_map.id != id) {
if (UNLIKELY(attr_map.id == ATTR_STD_NONE)) {
if (UNLIKELY(attr_map.element == 0)) {
return false;
}
/* Chain jump to a different part of the table. */
attr_offset = attr_map.offset;
}
else {
attr_offset += ATTR_PRIM_TYPES;
}
attr_map = attributes_map[attr_offset];
}
return true;
}
ccl_device_inline AttributeDescriptor find_attribute(const ccl_global AttributeMap *attributes_map,
uint attr_offset,
const int prim,
const uint64_t id)
{
if (!find_attr_offset(attributes_map, attr_offset, id)) {
return attribute_not_found();
}
const AttributeMap attr_map = attributes_map[attr_offset];
AttributeDescriptor desc;
desc.element = (AttributeElement)attr_map.element;
if (prim == PRIM_NONE &&
!(desc.element & (ATTR_ELEMENT_MESH | ATTR_ELEMENT_VOXEL | ATTR_ELEMENT_OBJECT)))
{
return attribute_not_found();
}
/* return result */
desc.offset = (attr_map.element == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND :
attr_map.offset;
desc.type = (NodeAttributeType)attr_map.type;
return desc;
}
ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg,
const int object,
const int prim,
const uint64_t id)
{
if (object == OBJECT_NONE) {
return attribute_not_found();
}
return find_attribute(
&kernel_data_fetch(attributes_map, 0), object_attribute_map_offset(kg, object), prim, id);
}
ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg,
const ccl_private ShaderData *sd,
const uint64_t id)
{
return find_attribute(kg, sd->object, sd->prim, id);
}
/* Templated functions to read from the attribute data */
template<typename T>
ccl_device_inline T attribute_data_fetch(KernelGlobals kg, AttributeElement element, int offset);
ccl_device_template_spec float attribute_data_fetch(KernelGlobals kg,
AttributeElement /*element*/,
int offset)
{
return kernel_data_fetch(attributes_float, offset);
}
ccl_device_template_spec float2 attribute_data_fetch(KernelGlobals kg,
AttributeElement /*element*/,
int offset)
{
return kernel_data_fetch(attributes_float2, offset);
}
ccl_device_template_spec float3 attribute_data_fetch(KernelGlobals kg,
AttributeElement element,
int offset)
{
if (element & ATTR_ELEMENT_IS_NORMAL) {
const packed_normal normal = kernel_data_fetch(attributes_normal, offset);
return normal.decode();
}
return kernel_data_fetch(attributes_float3, offset);
}
ccl_device_template_spec float4 attribute_data_fetch(KernelGlobals kg,
AttributeElement element,
int offset)
{
if (element & ATTR_ELEMENT_IS_BYTE) {
const float4 rec709 = color_srgb_to_linear_v4(
color_uchar4_to_float4(kernel_data_fetch(attributes_uchar4, offset)));
return make_float4(rec709_to_rgb(kg, make_float3(rec709)), rec709.w);
}
return kernel_data_fetch(attributes_float4, offset);
}
ccl_device_inline float3 attribute_data_fetch_normal(KernelGlobals kg, int offset)
{
const packed_normal normal = kernel_data_fetch(attributes_normal, offset);
return normal.decode();
}
ccl_device_inline void attribute_data_fetch_normals(KernelGlobals kg,
const int offset,
const int i0,
const int i1,
const int i2,
ccl_private float3 N[3])
{
#ifndef __KERNEL_GPU__
float4 nx, ny, nz;
const int4 packed_values = make_int4(kernel_data_fetch(attributes_normal, offset + i0).value,
kernel_data_fetch(attributes_normal, offset + i1).value,
kernel_data_fetch(attributes_normal, offset + i2).value,
0);
packed_normal_decode_simd(packed_values, nx, ny, nz);
N[0] = make_float3(nx.x, ny.x, nz.x);
N[1] = make_float3(nx.y, ny.y, nz.y);
N[2] = make_float3(nx.z, ny.z, nz.z);
#else
N[0] = attribute_data_fetch_normal(kg, offset + i0);
N[1] = attribute_data_fetch_normal(kg, offset + i1);
N[2] = attribute_data_fetch_normal(kg, offset + i2);
#endif
}
ccl_device_inline float3 attribute_data_interpolate_normals(KernelGlobals kg,
const int offset,
const int i0,
const int i1,
const int i2,
const float u,
const float v)
{
#ifndef __KERNEL_GPU__
float4 nx, ny, nz;
const int4 packed_values = make_int4(kernel_data_fetch(attributes_normal, offset + i0).value,
kernel_data_fetch(attributes_normal, offset + i1).value,
kernel_data_fetch(attributes_normal, offset + i2).value,
0);
packed_normal_decode_simd(packed_values, nx, ny, nz);
const float4 weights = make_float4(1.0f - u - v, u, v, 0.0f);
return make_float3(dot(nx, weights), dot(ny, weights), dot(nz, weights));
#else
const float3 n0 = attribute_data_fetch_normal(kg, offset + i0);
const float3 n1 = attribute_data_fetch_normal(kg, offset + i1);
const float3 n2 = attribute_data_fetch_normal(kg, offset + i2);
return (1.0f - u - v) * n0 + u * n1 + v * n2;
#endif
}
#ifdef __KERNEL_METAL__
template<typename U, typename V> using attribute_data_type_is_same = metal::is_same<U, V>;
#else
template<typename U, typename V> using attribute_data_type_is_same = std::is_same<U, V>;
#endif
template<typename T>
ccl_device_inline void attribute_data_fetch_3(KernelGlobals kg,
const AttributeElement element,
const int offset,
const int i0,
const int i1,
const int i2,
ccl_private T f[3])
{
if constexpr (attribute_data_type_is_same<T, float3>::value) {
if (element & ATTR_ELEMENT_IS_NORMAL) {
attribute_data_fetch_normals(kg, offset, i0, i1, i2, f);
}
else {
f[0] = kernel_data_fetch(attributes_float3, offset + i0);
f[1] = kernel_data_fetch(attributes_float3, offset + i1);
f[2] = kernel_data_fetch(attributes_float3, offset + i2);
}
}
else {
f[0] = attribute_data_fetch<T>(kg, element, offset + i0);
f[1] = attribute_data_fetch<T>(kg, element, offset + i1);
f[2] = attribute_data_fetch<T>(kg, element, offset + i2);
}
}
ccl_device_template_spec Transform attribute_data_fetch(KernelGlobals kg,
AttributeElement /*element*/,
int offset)
{
Transform tfm;
tfm.x = kernel_data_fetch(attributes_float4, offset + 0);
tfm.y = kernel_data_fetch(attributes_float4, offset + 1);
tfm.z = kernel_data_fetch(attributes_float4, offset + 2);
return tfm;
}
/* Transform matrix attribute on meshes */
ccl_device Transform primitive_attribute_matrix(KernelGlobals kg, const AttributeDescriptor desc)
{
return attribute_data_fetch<Transform>(kg, desc.element, desc.offset);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,239 @@
/* 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/motion_curve.h"
#include "kernel/geom/object.h"
CCL_NAMESPACE_BEGIN
/* Curve Primitive
*
* Curve primitive for rendering hair and fur. These can be render as flat
* ribbons or curves with actual thickness. The curve can also be rendered as
* line segments rather than curves for better performance.
*/
#ifdef __HAIR__
/* Partial derivative of f w.r.t. x, namely ∂f/∂x
* f is a function of u (along the curve)
* f(u) = f0 * (1 - u) + f1 * u,
* The partial derivative in x is
* ∂f/∂x = ∂f/∂u * ∂u/∂x
* = (f1 - f0) * du.dx. */
template<typename T>
ccl_device_inline T curve_attribute_dfdx(const ccl_private differential &du,
const ccl_private T &f0,
const ccl_private T &f1)
{
return du.dx * (f1 - f0);
}
/* Partial derivative of f w.r.t. in x, namely ∂f/∂y, similarly computed as ∂f/∂x above. */
template<typename T>
ccl_device_inline T curve_attribute_dfdy(const ccl_private differential &du,
const ccl_private T &f0,
const ccl_private T &f1)
{
return du.dy * (f1 - f0);
}
/* Read attributes on various curve elements. T is the return type, which can be a plain type
* or a dual type to include derivatives. */
template<typename T>
ccl_device T curve_attribute(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc)
{
using BaseT = dual_base_t<T>;
if (desc.element & ATTR_ELEMENT_CURVE_KEY) {
const KernelCurve curve = kernel_data_fetch(curves, sd->prim);
const int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
const int k1 = k0 + 1;
const BaseT f0 = attribute_data_fetch<BaseT>(kg, desc.element, desc.offset + k0);
const BaseT f1 = attribute_data_fetch<BaseT>(kg, desc.element, desc.offset + k1);
if constexpr (is_dual_v<T>) {
T result;
result.val = mix(f0, f1, sd->u);
# ifdef __RAY_DIFFERENTIALS__
result.dx = curve_attribute_dfdx(sd->du, f0, f1);
result.dy = curve_attribute_dfdy(sd->du, f0, f1);
# endif
return result;
}
else {
return mix(f0, f1, sd->u);
}
}
/* idea: we can't derive any useful differentials here, but for tiled
* mipmap image caching it would be useful to avoid reading the highest
* detail level always. maybe a derivative based on the hair density
* could be computed somehow? */
if (desc.element & ATTR_ELEMENT_CURVE) {
return T(attribute_data_fetch<BaseT>(kg, desc.element, desc.offset + sd->prim));
}
return make_zero<T>();
}
/* Curve thickness */
ccl_device float curve_thickness(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if (!(sd->type & PRIMITIVE_CURVE)) {
return 0.0f;
}
const KernelCurve curve = kernel_data_fetch(curves, sd->prim);
const int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
const int k1 = k0 + 1;
float4 P_curve[2];
# ifdef __OBJECT_MOTION__
if (sd->type & PRIMITIVE_MOTION) {
motion_curve_keys_linear(kg, sd->object, sd->time, k0, k1, P_curve);
}
else
# endif
{
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
P_curve[0] = kernel_data_fetch(curve_keys, position_offset + k0);
P_curve[1] = kernel_data_fetch(curve_keys, position_offset + k1);
}
float r = 2.0f * ((P_curve[1].w - P_curve[0].w) * sd->u + P_curve[0].w);
if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) {
return r;
}
const float normalized_r = r * (1.0f / M_SQRT3_F);
float3 dir = make_float3(normalized_r, normalized_r, normalized_r);
object_dir_transform(kg, sd, &dir);
return len(dir);
}
/* Curve random */
ccl_device float curve_random(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if (sd->type & PRIMITIVE_CURVE) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_CURVE_RANDOM);
return is_attribute_found(desc) ? curve_attribute<float>(kg, sd, desc) : 0.0f;
}
return 0.0f;
}
/* Curve location for motion pass, linear interpolation between keys and
* ignoring radius because we do the same for the motion keys */
ccl_device float3 curve_motion_center_location(KernelGlobals kg, const ccl_private ShaderData *sd)
{
const KernelCurve curve = kernel_data_fetch(curves, sd->prim);
const int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
const int k1 = k0 + 1;
float4 P_curve[2];
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
P_curve[0] = kernel_data_fetch(curve_keys, position_offset + k0);
P_curve[1] = kernel_data_fetch(curve_keys, position_offset + k1);
return make_float3(P_curve[1]) * sd->u + make_float3(P_curve[0]) * (1.0f - sd->u);
}
/* Curve tangent normal */
ccl_device float3 curve_tangent_normal(const ccl_private ShaderData *sd)
{
float3 tgN = make_float3(0.0f, 0.0f, 0.0f);
if (sd->type & PRIMITIVE_CURVE) {
tgN = -(-sd->wi - sd->dPdu * (dot(sd->dPdu, -sd->wi) / len_squared(sd->dPdu)));
tgN = normalize(tgN);
/* need to find suitable scaled gd for corrected normal */
# if 0
tgN = normalize(tgN - gd * sd->dPdu);
# endif
}
return tgN;
}
/* Curve bounds utility function */
ccl_device_inline void curvebounds(ccl_private float *lower,
ccl_private float *upper,
ccl_private float *extremta,
ccl_private float *extrema,
ccl_private float *extremtb,
ccl_private float *extremb,
float p0,
float p1,
float p2,
float p3)
{
float halfdiscroot = (p2 * p2 - 3 * p3 * p1);
float ta = -1.0f;
float tb = -1.0f;
*extremta = -1.0f;
*extremtb = -1.0f;
*upper = p0;
*lower = (p0 + p1) + (p2 + p3);
*extrema = *upper;
*extremb = *lower;
if (*lower >= *upper) {
*upper = *lower;
*lower = p0;
}
if (halfdiscroot >= 0) {
const float inv3p3 = (1.0f / 3.0f) / p3;
halfdiscroot = sqrtf(halfdiscroot);
ta = (-p2 - halfdiscroot) * inv3p3;
tb = (-p2 + halfdiscroot) * inv3p3;
}
float t2;
float t3;
if (ta > 0.0f && ta < 1.0f) {
t2 = ta * ta;
t3 = t2 * ta;
*extremta = ta;
*extrema = p3 * t3 + p2 * t2 + p1 * ta + p0;
*upper = fmaxf(*extrema, *upper);
*lower = fminf(*extrema, *lower);
}
if (tb > 0.0f && tb < 1.0f) {
t2 = tb * tb;
t3 = t2 * tb;
*extremtb = tb;
*extremb = p3 * t3 + p2 * t2 + p1 * tb + p0;
*upper = fmaxf(*extremb, *upper);
*lower = fminf(*extremb, *lower);
}
}
#endif /* __HAIR__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,958 @@
/* SPDX-FileCopyrightText: 2009-2021 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*
* Adapted from Embree with modifications. */
#pragma once
#include "kernel/geom/motion_curve.h"
#include "kernel/geom/object.h"
CCL_NAMESPACE_BEGIN
/* Curve primitive intersection functions.
*
* The code here was adapted from curve_intersector_sweep.h in Embree, to get
* an exact match between Embree CPU ray-tracing and our GPU ray-tracing. */
// NOLINTBEGIN
#define CURVE_NUM_BEZIER_SUBDIVISIONS 3
#define CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE (CURVE_NUM_BEZIER_SUBDIVISIONS + 1)
#define CURVE_NUM_BEZIER_STEPS 2
#define CURVE_NUM_JACOBIAN_ITERATIONS 5
// NOLINTEND
#ifdef __HAIR__
/* Catmull-rom curve evaluation. */
ccl_device_inline float4 catmull_rom_basis_eval(const float4 curve[4], float u)
{
const float t = u;
const float s = 1.0f - u;
const float n0 = -t * s * s;
const float n1 = 2.0f + t * t * (3.0f * t - 5.0f);
const float n2 = 2.0f + s * s * (3.0f * s - 5.0f);
const float n3 = -s * t * t;
return 0.5f * (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
}
ccl_device_inline float4 catmull_rom_basis_derivative(const float4 curve[4], float u)
{
const float t = u;
const float s = 1.0f - u;
const float n0 = -s * s + 2.0f * s * t;
const float n1 = 2.0f * t * (3.0f * t - 5.0f) + 3.0f * t * t;
const float n2 = 2.0f * s * (3.0f * t + 2.0f) - 3.0f * s * s;
const float n3 = -2.0f * s * t + t * t;
return 0.5f * (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
}
ccl_device_inline float4 catmull_rom_basis_derivative2(const float4 curve[4], float u)
{
const float t = u;
const float n0 = -3.0f * t + 2.0f;
const float n1 = 9.0f * t - 5.0f;
const float n2 = -9.0f * t + 4.0f;
const float n3 = 3.0f * t - 1.0f;
return (curve[0] * n0 + curve[1] * n1 + curve[2] * n2 + curve[3] * n3);
}
/* Thick Curve */
ccl_device_inline float3 dnormalize(const float3 p, const float3 dp)
{
const float pp = dot(p, p);
const float pdp = dot(p, dp);
return (pp * dp - pdp * p) / (pp * sqrtf(pp));
}
ccl_device_inline float sqr_point_to_line_distance(const float3 PmQ0, const float3 Q1mQ0)
{
const float3 N = cross(PmQ0, Q1mQ0);
const float3 D = Q1mQ0;
return dot(N, N) / dot(D, D);
}
ccl_device_inline bool cylinder_intersect(const float3 cylinder_start,
const float3 cylinder_end,
const float cylinder_radius,
const float3 ray_D,
ccl_private float2 *t_o,
ccl_private float *u0_o,
ccl_private float3 *Ng0_o,
ccl_private float *u1_o,
ccl_private float3 *Ng1_o)
{
/* Calculate quadratic equation to solve. */
const float rl = 1.0f / len(cylinder_end - cylinder_start);
const float3 P0 = cylinder_start;
const float3 dP = (cylinder_end - cylinder_start) * rl;
const float3 O = -P0;
const float3 dO = ray_D;
const float dOdO = dot(dO, dO);
const float OdO = dot(dO, O);
const float OO = dot(O, O);
const float dOz = dot(dP, dO);
const float Oz = dot(dP, O);
const float A = dOdO - sqr(dOz);
const float B = 2.0f * (OdO - dOz * Oz);
const float C = OO - sqr(Oz) - sqr(cylinder_radius);
/* We miss the cylinder if determinant is smaller than zero. */
const float D = B * B - 4.0f * A * C;
if (!(D >= 0.0f)) {
*t_o = make_float2(FLT_MAX, -FLT_MAX);
return false;
}
/* Special case for rays that are parallel to the cylinder. */
const float eps = 16.0f * FLT_EPSILON * max(fabsf(dOdO), fabsf(sqr(dOz)));
if (fabsf(A) < eps) {
if (C <= 0.0f) {
*t_o = make_float2(-FLT_MAX, FLT_MAX);
return true;
}
*t_o = make_float2(-FLT_MAX, FLT_MAX);
return false;
}
/* Standard case for rays that are not parallel to the cylinder. */
const float Q = sqrtf(D);
const float rcp_2A = 1.0f / (2.0f * A);
const float t0 = (-B - Q) * rcp_2A;
const float t1 = (-B + Q) * rcp_2A;
/* Calculates u and Ng for near hit. */
{
*u0_o = (t0 * dOz + Oz) * rl;
const float3 Pr = t0 * ray_D;
const float3 Pl = (*u0_o) * (cylinder_end - cylinder_start) + cylinder_start;
*Ng0_o = Pr - Pl;
}
/* Calculates u and Ng for far hit. */
{
*u1_o = (t1 * dOz + Oz) * rl;
const float3 Pr = t1 * ray_D;
const float3 Pl = (*u1_o) * (cylinder_end - cylinder_start) + cylinder_start;
*Ng1_o = Pr - Pl;
}
*t_o = make_float2(t0, t1);
return true;
}
ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, const float3 ray_D)
{
const float3 O = -P;
const float3 D = ray_D;
const float ON = dot(O, N);
const float DN = dot(D, N);
const float min_rcp_input = 1e-18f;
const bool eps = fabsf(DN) < min_rcp_input;
const float t = -ON / DN;
const float lower = (eps || DN < 0.0f) ? -FLT_MAX : t;
const float upper = (eps || DN > 0.0f) ? FLT_MAX : t;
return make_float2(lower, upper);
}
ccl_device bool curve_intersect_iterative(const float3 ray_D,
const float ray_tmin,
ccl_private float *ray_tmax,
const float dt,
const float4 curve[4],
float u,
float t,
const bool use_backfacing,
ccl_private Intersection *isect)
{
const float length_ray_D = len(ray_D);
/* Error of curve evaluations is proportional to largest coordinate. */
const float4 box_min = min(min(curve[0], curve[1]), min(curve[2], curve[3]));
const float4 box_max = max(min(curve[0], curve[1]), max(curve[2], curve[3]));
const float4 box_abs = max(fabs(box_min), fabs(box_max));
const float P_err = 16.0f * FLT_EPSILON *
max(box_abs.x, max(box_abs.y, max(box_abs.z, box_abs.w)));
const float radius_max = box_max.w;
for (int i = 0; i < CURVE_NUM_JACOBIAN_ITERATIONS; i++) {
const float3 Q = ray_D * t;
const float3 dQdt = ray_D;
const float Q_err = 16.0f * FLT_EPSILON * length_ray_D * t;
const float4 P4 = catmull_rom_basis_eval(curve, u);
const float4 dPdu4 = catmull_rom_basis_derivative(curve, u);
const float3 P = make_float3(P4);
const float3 dPdu = make_float3(dPdu4);
const float radius = P4.w;
const float dradiusdu = dPdu4.w;
const float3 ddPdu = make_float3(catmull_rom_basis_derivative2(curve, u));
const float3 R = Q - P;
const float len_R = len(R);
const float R_err = max(Q_err, P_err);
const float3 dRdu = -dPdu;
const float3 dRdt = dQdt;
const float3 T = normalize(dPdu);
const float3 dTdu = dnormalize(dPdu, ddPdu);
const float cos_err = P_err / len(dPdu);
const float f = dot(R, T);
const float f_err = len_R * P_err + R_err + cos_err * (1.0f + len_R);
const float dfdu = dot(dRdu, T) + dot(R, dTdu);
const float dfdt = dot(dRdt, T);
const float K = dot(R, R) - sqr(f);
const float dKdu = (dot(R, dRdu) - f * dfdu);
const float dKdt = (dot(R, dRdt) - f * dfdt);
const float rsqrt_K = inversesqrtf(K);
const float g = sqrtf(K) - radius;
const float g_err = R_err + f_err + 16.0f * FLT_EPSILON * radius_max;
const float dgdu = dKdu * rsqrt_K - dradiusdu;
const float dgdt = dKdt * rsqrt_K;
const float invdet = 1.0f / (dfdu * dgdt - dgdu * dfdt);
u -= (dgdt * f - dfdt * g) * invdet;
t -= (-dgdu * f + dfdu * g) * invdet;
if (fabsf(f) < f_err && fabsf(g) < g_err) {
t += dt;
if (!(t >= ray_tmin && t <= *ray_tmax)) {
return false; /* Rejects NaNs */
}
if (!(u >= 0.0f && u <= 1.0f)) {
return false; /* Rejects NaNs */
}
/* Back-face culling. */
const float3 R = normalize(Q - P);
const float3 U = dradiusdu * R + dPdu;
const float3 V = cross(dPdu, R);
const float3 Ng = cross(V, U);
if (!use_backfacing && dot(ray_D, Ng) > 0.0f) {
return false;
}
/* Record intersection. */
*ray_tmax = t;
isect->t = t;
isect->u = u;
isect->v = 0.0f;
return true;
}
}
return false;
}
ccl_device bool curve_intersect_recursive(const float3 ray_P,
const float3 ray_D,
const float ray_tmin,
float ray_tmax,
float4 curve[4],
ccl_private Intersection *isect)
{
/* Move ray closer to make intersection stable. */
const float3 center = make_float3(0.25f * (curve[0] + curve[1] + curve[2] + curve[3]));
const float dt = dot(center - ray_P, ray_D) / dot(ray_D, ray_D);
const float3 ref = ray_P + ray_D * dt;
const float4 ref4 = make_float4(ref, 0.0f);
curve[0] -= ref4;
curve[1] -= ref4;
curve[2] -= ref4;
curve[3] -= ref4;
const bool use_backfacing = false;
const float step_size = 1.0f / (float)(CURVE_NUM_BEZIER_STEPS);
int depth = 0;
/* todo: optimize stack for GPU somehow? Possibly some bitflags are enough, and
* u0/u1 can be derived from the depth. */
struct {
float u0, u1;
int i;
} stack[CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE];
bool found = false;
float u0 = 0.0f;
float u1 = 1.0f;
int i = 0;
while (true) {
for (; i < CURVE_NUM_BEZIER_STEPS; i++) {
const float step = i * step_size;
/* Subdivide curve. */
const float dscale = (u1 - u0) * (1.0f / 3.0f) * step_size;
const float vu0 = mix(u0, u1, step);
const float vu1 = mix(u0, u1, step + step_size);
const float4 P0 = catmull_rom_basis_eval(curve, vu0);
const float4 dP0du = dscale * catmull_rom_basis_derivative(curve, vu0);
const float4 P3 = catmull_rom_basis_eval(curve, vu1);
const float4 dP3du = dscale * catmull_rom_basis_derivative(curve, vu1);
const float4 P1 = P0 + dP0du;
const float4 P2 = P3 - dP3du;
/* Calculate bounding cylinders. */
const float rr1 = sqr_point_to_line_distance(make_float3(dP0du), make_float3(P3 - P0));
const float rr2 = sqr_point_to_line_distance(make_float3(dP3du), make_float3(P3 - P0));
const float maxr12 = sqrtf(max(rr1, rr2));
const float one_plus_ulp = 1.0f + 2.0f * FLT_EPSILON;
const float one_minus_ulp = 1.0f - 2.0f * FLT_EPSILON;
float r_outer = max(max(P0.w, P1.w), max(P2.w, P3.w)) + maxr12;
float r_inner = min(min(P0.w, P1.w), min(P2.w, P3.w)) - maxr12;
r_outer = one_plus_ulp * r_outer;
r_inner = max(0.0f, one_minus_ulp * r_inner);
bool valid = true;
/* Intersect with outer cylinder. */
float2 tc_outer;
float u_outer0;
float u_outer1;
float3 Ng_outer0;
float3 Ng_outer1;
valid = cylinder_intersect(make_float3(P0),
make_float3(P3),
r_outer,
ray_D,
&tc_outer,
&u_outer0,
&Ng_outer0,
&u_outer1,
&Ng_outer1);
if (!valid) {
continue;
}
/* Intersect with cap-planes. */
float2 tp = make_float2(ray_tmin - dt, ray_tmax - dt);
tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y));
const float2 h0 = half_plane_intersect(make_float3(P0), make_float3(dP0du), ray_D);
tp = make_float2(max(tp.x, h0.x), min(tp.y, h0.y));
const float2 h1 = half_plane_intersect(make_float3(P3), -make_float3(dP3du), ray_D);
tp = make_float2(max(tp.x, h1.x), min(tp.y, h1.y));
valid = tp.x <= tp.y;
if (!valid) {
continue;
}
/* Clamp and correct u parameter. */
u_outer0 = clamp(u_outer0, 0.0f, 1.0f);
u_outer1 = clamp(u_outer1, 0.0f, 1.0f);
u_outer0 = mix(u0, u1, (step + u_outer0) * (1.0f / (float)(CURVE_NUM_BEZIER_STEPS + 1)));
u_outer1 = mix(u0, u1, (step + u_outer1) * (1.0f / (float)(CURVE_NUM_BEZIER_STEPS + 1)));
/* Intersect with inner cylinder. */
float2 tc_inner;
float u_inner0;
float u_inner1;
float3 Ng_inner0;
float3 Ng_inner1;
const bool valid_inner = cylinder_intersect(make_float3(P0),
make_float3(P3),
r_inner,
ray_D,
&tc_inner,
&u_inner0,
&Ng_inner0,
&u_inner1,
&Ng_inner1);
/* At the unstable area we subdivide deeper. */
# if 0
const bool unstable0 = (!valid_inner) |
(fabsf(dot(normalize(ray_D), normalize(Ng_inner0))) < 0.3f);
const bool unstable1 = (!valid_inner) |
(fabsf(dot(normalize(ray_D), normalize(Ng_inner1))) < 0.3f);
# else
/* On the GPU appears to be a little faster if always enabled. */
(void)valid_inner;
const bool unstable0 = true;
const bool unstable1 = true;
# endif
/* Subtract the inner interval from the current hit interval. */
const float eps = 0.001f;
const float2 tp0 = make_float2(tp.x, min(tp.y, tc_inner.x));
const float2 tp1 = make_float2(max(tp.x, tc_inner.y), tp.y);
/* The X component should be less than the Y component for a valid intersection,
* but due to precision issues, the X component can sometimes be greater than
* Y by a small amount, leading to missing intersections. */
const bool valid0 = valid && ((tp0.x - tp0.y) < eps);
const bool valid1 = valid && ((tp1.x - tp1.y) < eps);
if (!(valid0 || valid1)) {
continue;
}
/* Process one or two hits. */
bool recurse = false;
if (valid0) {
const int termDepth = unstable0 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE :
CURVE_NUM_BEZIER_SUBDIVISIONS;
if (depth >= termDepth) {
found |= curve_intersect_iterative(
ray_D, ray_tmin, &ray_tmax, dt, curve, u_outer0, tp0.x, use_backfacing, isect);
}
else {
recurse = true;
}
}
const float t1 = tp1.x + dt;
if (valid1 && (t1 >= ray_tmin && t1 <= ray_tmax)) {
const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE :
CURVE_NUM_BEZIER_SUBDIVISIONS;
if (depth >= termDepth) {
found |= curve_intersect_iterative(
ray_D, ray_tmin, &ray_tmax, dt, curve, u_outer1, tp1.y, use_backfacing, isect);
}
else {
recurse = true;
}
}
if (recurse) {
stack[depth].u0 = u0;
stack[depth].u1 = u1;
stack[depth].i = i + 1;
depth++;
u0 = vu0;
u1 = vu1;
i = -1;
}
}
if (depth > 0) {
depth--;
u0 = stack[depth].u0;
u1 = stack[depth].u1;
i = stack[depth].i;
}
else {
break;
}
}
return found;
}
/* Ribbons */
ccl_device_inline bool cylinder_culling_test(const float2 p1, const float2 p2, const float r)
{
/* Performs culling against a cylinder. */
const float2 dp = p2 - p1;
const float num = dp.x * p1.y - dp.y * p1.x;
const float den2 = dot(dp, dp);
return num * num <= r * r * den2;
}
/**
* Intersects a ray with a quad with back-face culling
* enabled. The quad v0,v1,v2,v3 is split into two triangles
* v0,v1,v3 and v2,v3,v1. The edge v1,v2 decides which of the two
* triangles gets intersected.
*/
ccl_device_inline bool ribbon_intersect_quad(const float ray_tmin,
const float ray_tmax,
const float3 quad_v0,
const float3 quad_v1,
const float3 quad_v2,
const float3 quad_v3,
ccl_private float *u_o,
ccl_private float *v_o,
ccl_private float *t_o)
{
/* Calculate vertices relative to ray origin? */
const float3 O = make_float3(0.0f, 0.0f, 0.0f);
const float3 D = make_float3(0.0f, 0.0f, 1.0f);
const float3 va = quad_v0 - O;
const float3 vb = quad_v1 - O;
const float3 vc = quad_v2 - O;
const float3 vd = quad_v3 - O;
const float3 edb = vb - vd;
const float WW = dot(cross(vd, edb), D);
const float3 v0 = (WW <= 0.0f) ? va : vc;
const float3 v1 = (WW <= 0.0f) ? vb : vd;
const float3 v2 = (WW <= 0.0f) ? vd : vb;
/* Calculate edges? */
const float3 e0 = v2 - v0;
const float3 e1 = v0 - v1;
/* perform edge tests */
const float U = dot(cross(v0, e0), D);
const float V = dot(cross(v1, e1), D);
if (!(max(U, V) <= 0.0f)) {
return false;
}
/* Calculate geometry normal and denominator? */
const float3 Ng = cross(e1, e0);
const float den = dot(Ng, D);
const float rcpDen = 1.0f / den;
/* Perform depth test? */
const float t = rcpDen * dot(v0, Ng);
if (!(t >= ray_tmin && t <= ray_tmax)) {
return false;
}
/* Avoid division by 0? */
if (!(den != 0.0f)) {
return false;
}
/* Update hit information? */
*t_o = t;
*u_o = U * rcpDen;
*v_o = V * rcpDen;
*u_o = (WW <= 0.0f) ? *u_o : 1.0f - *u_o;
*v_o = (WW <= 0.0f) ? *v_o : 1.0f - *v_o;
return true;
}
ccl_device_inline void ribbon_ray_space(const float3 ray_D,
const float ray_D_invlen,
float3 ray_space[3])
{
const float3 D = ray_D * ray_D_invlen;
const float3 dx0 = make_float3(0, D.z, -D.y);
const float3 dx1 = make_float3(-D.z, 0, D.x);
ray_space[0] = normalize(dot(dx0, dx0) > dot(dx1, dx1) ? dx0 : dx1);
ray_space[1] = normalize(cross(D, ray_space[0]));
ray_space[2] = D * ray_D_invlen;
}
ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3],
const float3 ray_org,
const float4 P4)
{
const float3 P = make_float3(P4) - ray_org;
return make_float4(dot(ray_space[0], P), dot(ray_space[1], P), dot(ray_space[2], P), P4.w);
}
ccl_device_inline bool ribbon_intersect(const float3 ray_org,
const float3 ray_D,
const float ray_tmin,
float ray_tmax,
const int N,
float4 curve[4],
ccl_private Intersection *isect)
{
/* Transform control points into ray space. */
const float ray_D_invlen = 1.0f / len(ray_D);
float3 ray_space[3];
ribbon_ray_space(ray_D, ray_D_invlen, ray_space);
curve[0] = ribbon_to_ray_space(ray_space, ray_org, curve[0]);
curve[1] = ribbon_to_ray_space(ray_space, ray_org, curve[1]);
curve[2] = ribbon_to_ray_space(ray_space, ray_org, curve[2]);
curve[3] = ribbon_to_ray_space(ray_space, ray_org, curve[3]);
const float4 mx = max(max(fabs(curve[0]), fabs(curve[1])), max(fabs(curve[2]), fabs(curve[3])));
const float eps = 4.0f * FLT_EPSILON * max(max(mx.x, mx.y), max(mx.z, mx.w));
const float step_size = 1.0f / (float)N;
/* Evaluate first point and radius scaled normal direction. */
float4 p0 = catmull_rom_basis_eval(curve, 0.0f);
float3 dp0dt = make_float3(catmull_rom_basis_derivative(curve, 0.0f));
if (reduce_max(fabs(dp0dt)) < eps) {
const float4 p1 = catmull_rom_basis_eval(curve, step_size);
dp0dt = make_float3(p1 - p0);
}
float3 wn0 = normalize(make_float3(dp0dt.y, -dp0dt.x, 0.0f)) * p0.w;
/* Evaluate the bezier curve. */
for (int i = 0; i < N; i++) {
const float u = i * step_size;
const float4 p1 = catmull_rom_basis_eval(curve, u + step_size);
const bool valid = cylinder_culling_test(
make_float2(p0.x, p0.y), make_float2(p1.x, p1.y), max(p0.w, p1.w));
/* Evaluate next point. */
float3 dp1dt = make_float3(catmull_rom_basis_derivative(curve, u + step_size));
dp1dt = (reduce_max(fabs(dp1dt)) < eps) ? make_float3(p1 - p0) : dp1dt;
const float3 wn1 = normalize(make_float3(dp1dt.y, -dp1dt.x, 0.0f)) * p1.w;
if (valid) {
/* Construct quad coordinates. */
const float3 lp0 = make_float3(p0) + wn0;
const float3 lp1 = make_float3(p1) + wn1;
const float3 up0 = make_float3(p0) - wn0;
const float3 up1 = make_float3(p1) - wn1;
/* Intersect quad. */
float vu;
float vv;
float vt;
bool valid0 = ribbon_intersect_quad(ray_tmin, ray_tmax, lp0, lp1, up1, up0, &vu, &vv, &vt);
if (valid0) {
/* ignore self intersections */
const float avoidance_factor = 2.0f;
if (avoidance_factor != 0.0f) {
const float r = mix(p0.w, p1.w, vu);
valid0 = vt > avoidance_factor * r * ray_D_invlen;
}
if (valid0) {
vv = 2.0f * vv - 1.0f;
/* Record intersection. */
ray_tmax = vt;
isect->t = vt;
isect->u = u + vu * step_size;
isect->v = vv;
return true;
}
}
}
/* Store point for next step. */
p0 = p1;
wn0 = wn1;
}
return false;
}
/* Linear curve evaluation. */
ccl_device_inline float4 linear_basis_eval(const float4 curve[4], float u)
{
return mix(curve[1], curve[2], u);
}
ccl_device_inline float4 linear_basis_derivative(const float4 curve[4], float)
{
return curve[2] - curve[1];
}
/* Linear Thick Curve */
ccl_device_inline bool cone_sphere_intersect(const float4 curve[4],
const float3 ray_D,
ccl_private float *t_o,
ccl_private float *u_o,
ccl_private float3 *Ng_o)
{
/* Calculate quadratic equation to solve. */
const float r0 = curve[1].w;
const float r1 = curve[2].w;
const float dr = r1 - r0;
const float r0dr = r0 * dr;
const float3 P0 = make_float3(curve[1]);
const float3 P1 = make_float3(curve[2]);
const float3 dP = P1 - P0;
const float3 O = -P0;
const float3 dO = ray_D;
const float dOdO = dot(dO, dO);
const float OdO = dot(dO, O);
const float OO = dot(O, O);
const float dOz = dot(dP, dO);
const float Oz = dot(dP, O);
const float dPdP = dot(dP, dP);
const float yp = Oz + r0dr;
const float g = dPdP - sqr(dr);
const float A = g * dOdO - sqr(dOz);
const float B = 2.0f * (g * OdO - dOz * yp);
const float C = g * OO - sqr(Oz) - sqr(r0) * dPdP - 2.0f * r0dr * Oz;
/* We miss the cone if determinant is smaller than zero. */
const float D = B * B - 4.0f * A * C;
if (!(D >= 0.0f)) {
*t_o = FLT_MAX;
return false;
}
/* Special case for rays that are parallel to the cone. */
const float eps = 1e-18f;
if (fabsf(A) < eps) {
*t_o = -FLT_MAX;
return false;
}
/* Standard case for rays that are not parallel to the cone. */
const float Q = sqrtf(D);
const float rcp_2A = 1.0f / (2.0f * A);
const float t0 = (-B - Q) * rcp_2A;
const float y0 = yp + t0 * dOz;
float t = FLT_MAX;
/* Calculates u and Ng for near hit. */
if ((y0 > -FLT_EPSILON) && (y0 <= g) && (g > 0.0f)) {
t = t0;
*u_o = clamp(y0 / g, 0.0f, 1.0f);
const float3 Pr = O + t0 * dO;
const float3 Pl = (*u_o) * dP;
*Ng_o = Pr - Pl;
}
/* Intersect ending sphere. */
{
const float3 O1 = -P1;
const float O1dO = dot(O1, dO);
const float h2 = sqr(O1dO) - dOdO * (dot(O1, O1) - sqr(r1));
if (h2 >= 0.0f) {
const float rhs1 = sqrt(h2);
/* Clip away near hit if it is inside next cone segment. */
const float t_sph1 = (-O1dO - rhs1) * (1.0f / dOdO);
const float r2 = curve[3].w;
const float3 P2 = make_float3(curve[3]);
const float y2 = dot((t_sph1 * dO) - P1, (P2 - P1));
const float cap2 = -(r1 * (r2 - r1));
if ((t_sph1 <= t) && (yp + t_sph1 * dOz) > g && !(y2 > cap2)) {
t = t_sph1;
*u_o = 1.0f;
*Ng_o = t * dO - P1;
}
}
}
/* Intersect start sphere. */
if (isequal(curve[0], curve[1])) {
const float h2 = sqr(OdO) - dOdO * (dot(O, O) - sqr(r0));
if (h2 >= 0.0f) {
const float rhs1 = sqrt(h2);
/* Clip away near hit if it is inside next cone segment. */
const float t_sph0 = (-OdO - rhs1) * (1.0f / dOdO);
if ((t_sph0 <= t) && (yp + t_sph0 * dOz) < 0) {
t = t_sph0;
*u_o = 0.0f;
*Ng_o = t * dO - P0;
}
}
}
*t_o = t;
return t != FLT_MAX;
}
ccl_device bool linear_curve_intersect(const float3 ray_P,
const float3 ray_D,
const float ray_tmin,
float ray_tmax,
float4 curve[4],
ccl_private Intersection *isect)
{
/* Move ray closer to make intersection stable. */
const float3 center = make_float3(0.5f * (curve[1] + curve[2]));
const float dt = dot(center - ray_P, ray_D) / dot(ray_D, ray_D);
const float3 ref = ray_P + ray_D * dt;
const float4 ref4 = make_float4(ref, 0.0f);
curve[0] -= ref4;
curve[1] -= ref4;
curve[2] -= ref4;
curve[3] -= ref4;
/* Intersect with cone sphere. */
float t;
float u;
float3 Ng;
if (!cone_sphere_intersect(curve, ray_D, &t, &u, &Ng)) {
return false;
}
t += dt;
if (!(t >= ray_tmin && t <= ray_tmax)) {
return false; /* Rejects NaNs */
}
/* Record intersection. */
isect->t = t;
isect->u = u;
isect->v = 0.0f;
return true;
}
ccl_device_forceinline bool curve_intersect(KernelGlobals kg,
ccl_private Intersection *isect,
const float3 ray_P,
const float3 ray_D,
const float tmin,
const float tmax,
const int object,
const int prim,
const float time,
const int type)
{
const bool is_motion = (type & PRIMITIVE_MOTION);
const KernelCurve kcurve = kernel_data_fetch(curves, prim);
const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type);
const int k1 = k0 + 1;
const int ka = max(k0 - 1, kcurve.first_key);
const int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
float4 curve[4];
if (!is_motion) {
const int position_offset = kernel_data_fetch(objects, object).position_offset;
curve[0] = kernel_data_fetch(curve_keys, position_offset + ka);
curve[1] = kernel_data_fetch(curve_keys, position_offset + k0);
curve[2] = kernel_data_fetch(curve_keys, position_offset + k1);
curve[3] = kernel_data_fetch(curve_keys, position_offset + kb);
}
else {
motion_curve_keys(kg, object, time, ka, k0, k1, kb, curve);
}
switch (type & PRIMITIVE_CURVE) {
case PRIMITIVE_CURVE_RIBBON: {
/* todo: adaptive number of subdivisions could help performance here. */
const int subdivisions = kernel_data.bvh.curve_subdivisions;
if (ribbon_intersect(ray_P, ray_D, tmin, tmax, subdivisions, curve, isect)) {
isect->prim = prim;
isect->object = object;
isect->type = type;
return true;
}
break;
}
case PRIMITIVE_CURVE_THICK: {
if (curve_intersect_recursive(ray_P, ray_D, tmin, tmax, curve, isect)) {
isect->prim = prim;
isect->object = object;
isect->type = type;
return true;
}
break;
}
case PRIMITIVE_CURVE_THICK_LINEAR: {
if (linear_curve_intersect(ray_P, ray_D, tmin, tmax, curve, isect)) {
isect->prim = prim;
isect->object = object;
isect->type = type;
return true;
}
break;
}
}
return false;
}
ccl_device_inline void curve_shader_setup(KernelGlobals kg,
ccl_private ShaderData *sd,
float3 P,
float3 D,
float t,
const int isect_prim)
{
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
const Transform tfm = object_get_inverse_transform(kg, sd);
P = transform_point(&tfm, P);
D = transform_direction(&tfm, D * t);
D = safe_normalize_len(D, &t);
}
const KernelCurve kcurve = kernel_data_fetch(curves, isect_prim);
const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
const int k1 = k0 + 1;
const int ka = max(k0 - 1, kcurve.first_key);
const int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
float4 P_curve[4];
if (!(sd->type & PRIMITIVE_MOTION)) {
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
P_curve[0] = kernel_data_fetch(curve_keys, position_offset + ka);
P_curve[1] = kernel_data_fetch(curve_keys, position_offset + k0);
P_curve[2] = kernel_data_fetch(curve_keys, position_offset + k1);
P_curve[3] = kernel_data_fetch(curve_keys, position_offset + kb);
}
else {
motion_curve_keys(kg, sd->object, sd->time, ka, k0, k1, kb, P_curve);
}
P = P + D * t;
const float4 dPdu4 = (sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_THICK_LINEAR ?
linear_basis_derivative(P_curve, sd->u) :
catmull_rom_basis_derivative(P_curve, sd->u);
const float3 dPdu = make_float3(dPdu4);
if ((sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) {
/* Rounded smooth normals for ribbons, to approximate thick curve shape. */
const float3 tangent = normalize(dPdu);
const float3 bitangent = normalize(cross(tangent, -D));
const float sine = sd->v;
const float cosine = cos_from_sin(sine);
sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent)));
# if 0
/* This approximates the position and geometric normal of a thick curve too,
* but gives too many issues with wrong self intersections. */
const float dPdu_radius = dPdu4.w;
sd->Ng = sd->N;
P += sd->N * dPdu_radius;
# endif
}
else {
/* Thick curves, compute normal using direction from inside the curve.
* This could be optimized by recording the normal in the intersection,
* however for Optix this would go beyond the size of the payload. */
/* NOTE: It is possible that P will be the same as P_inside (precision issues, or very small
* radius). In this case use the view direction to approximate the normal. */
const float3 P_inside = (sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_THICK_LINEAR ?
make_float3(linear_basis_eval(P_curve, sd->u)) :
make_float3(catmull_rom_basis_eval(P_curve, sd->u));
const float3 N = (!isequal(P, P_inside)) ? normalize(P - P_inside) : -sd->wi;
sd->N = N;
sd->v = 0.0f;
}
# ifdef __DPDU__
/* dPdu/dPdv */
sd->dPdu = dPdu;
# endif
/* Convert to world space. */
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &P);
object_normal_transform(kg, sd, &sd->N);
object_dir_transform(kg, sd, &sd->dPdu);
}
sd->P = P;
sd->Ng = ((sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_RIBBON) ? sd->wi : sd->N;
sd->dPdv = cross(sd->dPdu, sd->Ng);
sd->shader = kernel_data_fetch(curves, sd->prim).shader_id;
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,64 @@
/* SPDX-FileCopyrightText: 2025 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Common utilities for various geometry type intersections. */
#pragma once
#include "kernel/globals.h"
#include "kernel/sample/lcg.h"
CCL_NAMESPACE_BEGIN
/* For an intersection with the given distance isect_t from the ray origin, increase the number
* of hits (when needed) and return an index within local_isect->hits where the intersection is to
* be stored. If the return value -1, then the intersection is to be ignored (nothing is to be
* written to the local_isect->hits, and the intersection test function is to return false.
*
* The LocalIntersection is a templated type, allowing different types that implement similar data
* layout to be passed here. This is needed for the MetalRT, where it is not possible to access the
* pointer to actual LocalIntersection from the git function. */
#ifdef __BVH_LOCAL__
template<class LocalIntersection>
ccl_device_forceinline int local_intersect_get_record_index(
ccl_private LocalIntersection *local_isect,
const float isect_t,
ccl_private uint *lcg_state,
const int max_hits)
{
if (lcg_state) {
/* Record up to max_hits intersections. */
for (int i = min(max_hits, int(local_isect->num_hits)) - 1; i >= 0; --i) {
if (local_isect->hits[i].t == isect_t) {
return -1;
}
}
local_isect->num_hits++;
int hit;
if (local_isect->num_hits <= max_hits) {
hit = local_isect->num_hits - 1;
}
else {
/* Reservoir sampling: if we are at the maximum number of hits, randomly replace element or
* skip it. */
hit = lcg_step_uint(lcg_state) % local_isect->num_hits;
if (hit >= max_hits) {
return -1;
}
}
return hit;
}
/* Record closest intersection only. */
if (local_isect->num_hits && isect_t > local_isect->hits[0].t) {
return -1;
}
local_isect->num_hits = 1;
return 0;
}
#endif /* __BVH_LOCAL__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,142 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/bvh/util.h"
CCL_NAMESPACE_BEGIN
/* Motion Curve Primitive
*
* These are stored as regular curves, plus extra positions and radii at times
* other than the frame center. Computing the curve keys at a given ray time is
* a matter of interpolation of the two steps between which the ray time lies.
*
* The extra curve keys are stored as additional motion steps in ATTR_STD_POSITION.
*/
#ifdef __HAIR__
ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals kg,
int offset,
const int numverts,
const int numsteps,
int step,
const int k0,
const int k1,
float4 keys[2])
{
const int center_step = (numsteps - 1) / 2;
if (step == center_step) {
/* Center step: first in the array. */
}
else {
/* Non-center step, stored after center with center index skipped. */
if (step < center_step) {
step++;
}
offset += step * numverts;
}
keys[0] = kernel_data_fetch(curve_keys, offset + k0);
keys[1] = kernel_data_fetch(curve_keys, offset + k1);
}
/* return 2 curve key locations */
ccl_device_inline void motion_curve_keys_linear(KernelGlobals kg,
const int object,
const float time,
const int k0,
const int k1,
float4 keys[2])
{
/* get motion info */
const int numsteps = kernel_data_fetch(objects, object).num_geom_steps;
const int numverts = kernel_data_fetch(objects, object).numverts;
/* figure out which steps we need to fetch and their interpolation factor */
const int maxstep = numsteps - 1;
const int step = min((int)(time * maxstep), maxstep - 1);
const float t = time * maxstep - step;
/* fetch key coordinates */
const int offset = kernel_data_fetch(objects, object).position_offset;
float4 next_keys[2];
motion_curve_keys_for_step_linear(kg, offset, numverts, numsteps, step, k0, k1, keys);
motion_curve_keys_for_step_linear(kg, offset, numverts, numsteps, step + 1, k0, k1, next_keys);
/* interpolate between steps */
keys[0] = (1.0f - t) * keys[0] + t * next_keys[0];
keys[1] = (1.0f - t) * keys[1] + t * next_keys[1];
}
ccl_device_inline void motion_curve_keys_for_step(KernelGlobals kg,
int offset,
const int numverts,
const int numsteps,
int step,
const int k0,
const int k1,
const int k2,
const int k3,
float4 keys[4])
{
const int center_step = (numsteps - 1) / 2;
if (step == center_step) {
/* Center step: first in the array. */
}
else {
/* Non-center step, stored after center with center index skipped. */
if (step < center_step) {
step++;
}
offset += step * numverts;
}
keys[0] = kernel_data_fetch(curve_keys, offset + k0);
keys[1] = kernel_data_fetch(curve_keys, offset + k1);
keys[2] = kernel_data_fetch(curve_keys, offset + k2);
keys[3] = kernel_data_fetch(curve_keys, offset + k3);
}
/* return 2 curve key locations */
ccl_device_inline void motion_curve_keys(KernelGlobals kg,
const int object,
const float time,
const int k0,
const int k1,
const int k2,
const int k3,
float4 keys[4])
{
/* get motion info */
const int numsteps = kernel_data_fetch(objects, object).num_geom_steps;
const int numverts = kernel_data_fetch(objects, object).numverts;
/* figure out which steps we need to fetch and their interpolation factor */
const int maxstep = numsteps - 1;
const int step = min((int)(time * maxstep), maxstep - 1);
const float t = time * maxstep - step;
/* fetch key coordinates */
const int offset = kernel_data_fetch(objects, object).position_offset;
float4 next_keys[4];
motion_curve_keys_for_step(kg, offset, numverts, numsteps, step, k0, k1, k2, k3, keys);
motion_curve_keys_for_step(kg, offset, numverts, numsteps, step + 1, k0, k1, k2, k3, next_keys);
/* interpolate between steps */
keys[0] = (1.0f - t) * keys[0] + t * next_keys[0];
keys[1] = (1.0f - t) * keys[1] + t * next_keys[1];
keys[2] = (1.0f - t) * keys[2] + t * next_keys[2];
keys[3] = (1.0f - t) * keys[3] + t * next_keys[3];
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,68 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/bvh/util.h"
CCL_NAMESPACE_BEGIN
/* Motion Point Primitive
*
* These are stored as regular points, plus extra positions and radii at times
* other than the frame center. Computing the point at a given ray time is
* a matter of interpolation of the two steps between which the ray time lies.
*
* The extra points are stored as additional motion steps in ATTR_STD_POSITION.
*/
#ifdef __POINTCLOUD__
ccl_device_inline float4 motion_point_for_step(
KernelGlobals kg, int offset, const int numverts, const int numsteps, int step, const int prim)
{
const int center_step = (numsteps - 1) / 2;
if (step == center_step) {
/* Center step: first in the array. */
}
else {
/* Non-center step, stored after center with center index skipped. */
if (step < center_step) {
step++;
}
offset += step * numverts;
}
return kernel_data_fetch(points, offset + prim);
}
/* return 2 point key locations */
ccl_device_inline float4 motion_point(KernelGlobals kg,
const int object,
const int prim,
const float time)
{
/* get motion info */
const int numsteps = kernel_data_fetch(objects, object).num_geom_steps;
const int numverts = kernel_data_fetch(objects, object).numverts;
/* figure out which steps we need to fetch and their interpolation factor */
const int maxstep = numsteps - 1;
const int step = min((int)(time * maxstep), maxstep - 1);
const float t = time * maxstep - step;
/* fetch key coordinates */
const int offset = kernel_data_fetch(objects, object).position_offset;
const float4 point = motion_point_for_step(kg, offset, numverts, numsteps, step, prim);
const float4 next_point = motion_point_for_step(kg, offset, numverts, numsteps, step + 1, prim);
/* interpolate between steps */
return (1.0f - t) * point + t * next_point;
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,260 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Motion Triangle Primitive
*
* These are stored as regular triangles, plus extra positions and normals at
* times other than the frame center. Computing the triangle vertex positions
* or normals at a given ray time is a matter of interpolation of the two steps
* between which the ray time lies.
*
* The extra positions are stored as additional motion steps in ATTR_STD_POSITION,
* normals in ATTR_STD_VERTEX_NORMAL or ATTR_STD_CORNER_NORMAL.
*/
#pragma once
#include "kernel/bvh/util.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/triangle.h"
CCL_NAMESPACE_BEGIN
/* Time interpolation of vertex positions and normals */
ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals kg,
const uint3 tri_vindex,
int offset,
const int numverts,
const int numsteps,
int step,
float3 verts[3])
{
const int center_step = (numsteps - 1) / 2;
if (step == center_step) {
/* Center step: first in the array. */
}
else {
/* Non-center step, stored after center with center index skipped. */
if (step < center_step) {
step++;
}
offset += step * numverts;
}
verts[0] = kernel_data_fetch(tri_verts, offset + tri_vindex.x);
verts[1] = kernel_data_fetch(tri_verts, offset + tri_vindex.y);
verts[2] = kernel_data_fetch(tri_verts, offset + tri_vindex.z);
}
ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals kg,
const int object,
const int object_flag,
const int prim,
const uint3 tri_vindex,
int offset,
const int numsteps,
int step,
float3 normals[3])
{
const int center_step = (numsteps - 1) / 2;
if (step == center_step) {
/* Center step: first in the array. */
}
else {
/* Non-center step: stored after center with center index skipped. */
int stride;
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
stride = kernel_data_fetch(objects, object).numprims * 3;
}
else {
stride = kernel_data_fetch(objects, object).numverts;
}
if (step < center_step) {
step++;
}
offset += step * stride;
}
int i0, i1, i2;
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
i0 = prim * 3 + 0;
i1 = prim * 3 + 1;
i2 = prim * 3 + 2;
}
else {
i0 = tri_vindex.x;
i1 = tri_vindex.y;
i2 = tri_vindex.z;
}
attribute_data_fetch_normals(kg, offset, i0, i1, i2, normals);
}
ccl_device_inline void motion_triangle_compute_info(KernelGlobals kg,
const int object,
const float time,
const int prim,
ccl_private uint3 *tri_vindex,
ccl_private int *numsteps,
ccl_private int *step,
ccl_private float *t)
{
/* Get object motion info. */
*numsteps = kernel_data_fetch(objects, object).num_geom_steps;
/* Figure out which steps we need to fetch and their interpolation factor. */
const int maxstep = *numsteps - 1;
*step = min((int)(time * maxstep), maxstep - 1);
*t = time * maxstep - *step;
/* Get triangle indices. */
*tri_vindex = kernel_data_fetch(tri_vindex, prim);
}
ccl_device_inline void motion_triangle_vertices(KernelGlobals kg,
const int object,
const uint3 tri_vindex,
const int numsteps,
const int numverts,
const int step,
const float t,
float3 verts[3])
{
/* Fetch vertex coordinates. */
const int offset = kernel_data_fetch(objects, object).position_offset;
float3 next_verts[3];
motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step, verts);
motion_triangle_verts_for_step(kg, tri_vindex, offset, numverts, numsteps, step + 1, next_verts);
/* Interpolate between steps. */
verts[0] = (1.0f - t) * verts[0] + t * next_verts[0];
verts[1] = (1.0f - t) * verts[1] + t * next_verts[1];
verts[2] = (1.0f - t) * verts[2] + t * next_verts[2];
}
ccl_device_inline void motion_triangle_vertices(
KernelGlobals kg, const int object, const int prim, const float time, float3 verts[3])
{
int numsteps;
int step;
float t;
uint3 tri_vindex;
motion_triangle_compute_info(kg, object, time, prim, &tri_vindex, &numsteps, &step, &t);
const int numverts = kernel_data_fetch(objects, object).numverts;
motion_triangle_vertices(kg, object, tri_vindex, numsteps, numverts, step, t, verts);
}
ccl_device_inline void motion_triangle_normals(KernelGlobals kg,
const int object,
const int prim,
const uint3 tri_vindex,
const int numsteps,
const int step,
const float t,
float3 normals[3])
{
/* Fetch normals. */
const int object_flag = kernel_data_fetch(object_flag, object);
const int offset = kernel_data_fetch(objects, object).normal_offset;
float3 next_normals[3];
motion_triangle_normals_for_step(
kg, object, object_flag, prim, tri_vindex, offset, numsteps, step, normals);
motion_triangle_normals_for_step(
kg, object, object_flag, prim, tri_vindex, offset, numsteps, step + 1, next_normals);
/* Interpolate between steps. */
normals[0] = normalize((1.0f - t) * normals[0] + t * next_normals[0]);
normals[1] = normalize((1.0f - t) * normals[1] + t * next_normals[1]);
normals[2] = normalize((1.0f - t) * normals[2] + t * next_normals[2]);
}
ccl_device_inline void motion_triangle_vertices_and_normals(KernelGlobals kg,
const ccl_private ShaderData *sd,
float3 verts[3],
float3 normals[3])
{
const int object = sd->object;
int numsteps, step;
float t;
uint3 tri_vindex;
motion_triangle_compute_info(kg, object, sd->time, sd->prim, &tri_vindex, &numsteps, &step, &t);
const int numverts = kernel_data_fetch(objects, object).numverts;
motion_triangle_vertices(kg, object, tri_vindex, numsteps, numverts, step, t, verts);
motion_triangle_normals(kg, object, sd->prim, tri_vindex, numsteps, step, t, normals);
}
ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
const float3 Ng,
const int object,
const int prim,
const uint3 tri_vindex,
const int numsteps,
const int step,
const float t,
const float u,
const float v)
{
float3 normals[3];
motion_triangle_normals(kg, object, prim, tri_vindex, numsteps, step, t, normals);
/* Interpolate between normals. */
const float w = 1.0f - u - v;
const float3 N = safe_normalize(w * normals[0] + u * normals[1] + v * normals[2]);
return is_zero(N) ? Ng : N;
}
ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
const float3 Ng,
const int object,
const int prim,
const float u,
float v,
const float time)
{
int numsteps;
int step;
float t;
uint3 tri_vindex;
motion_triangle_compute_info(kg, object, time, prim, &tri_vindex, &numsteps, &step, &t);
return motion_triangle_smooth_normal(kg, Ng, object, prim, tri_vindex, numsteps, step, t, u, v);
}
/* Compute motion triangle normals at the hit position, and offsetted positions in x and y
* direction for bump mapping. */
ccl_device_inline float3 motion_triangle_smooth_normal(KernelGlobals kg,
const float3 Ng,
const int object,
const int prim,
const float time,
const float u,
const float v,
const differential du,
const differential dv,
ccl_private float3 &N_x,
ccl_private float3 &N_y)
{
int numsteps, step;
float t;
uint3 tri_vindex;
motion_triangle_compute_info(kg, object, time, prim, &tri_vindex, &numsteps, &step, &t);
float3 n[3];
motion_triangle_normals(kg, object, prim, tri_vindex, numsteps, step, t, n);
const float3 N = safe_normalize(triangle_interpolate(u, v, n[0], n[1], n[2]));
N_x = safe_normalize(triangle_interpolate(u + du.dx, v + dv.dx, n[0], n[1], n[2]));
N_y = safe_normalize(triangle_interpolate(u + du.dy, v + dv.dy, n[0], n[1], n[2]));
N_x = is_zero(N_x) ? Ng : N_x;
N_y = is_zero(N_y) ? Ng : N_y;
return is_zero(N) ? Ng : N;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,127 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Motion Triangle Primitive
*
* These are stored as regular triangles, plus extra positions and normals at
* times other than the frame center. Computing the triangle vertex positions
* or normals at a given ray time is a matter of interpolation of the two steps
* between which the ray time lies.
*
* The extra positions are stored as additional motion steps in ATTR_STD_POSITION.
* Normals in ATTR_STD_VERTEX_NORMAL and ATTR_STD_CORNER_NORMAL.
*/
#pragma once
#include "kernel/globals.h"
#include "kernel/types.h"
#include "kernel/geom/geom_intersect.h"
#include "kernel/geom/motion_triangle.h"
#include "kernel/geom/object.h"
#include "util/math_intersect.h"
CCL_NAMESPACE_BEGIN
/* Ray intersection. We simply compute the vertex positions at the given ray
* time and do a ray intersection with the resulting triangle.
*/
ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg,
ccl_private Intersection *isect,
const float3 P,
const float3 dir,
const float tmin,
const float tmax,
const float time,
const uint visibility,
const int object,
const int prim,
const int prim_addr)
{
/* Get vertex locations for intersection. */
float3 verts[3];
motion_triangle_vertices(kg, object, prim, time, verts);
/* Ray-triangle intersection, unoptimized. */
float t;
float u;
float v;
if (ray_triangle_intersect(P, dir, tmin, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) {
#ifdef __VISIBILITY_FLAG__
/* Visibility flag test. we do it here under the assumption
* that most triangles are culled by node flags.
*/
if (kernel_data_fetch(prim_visibility, prim_addr) & visibility)
#endif
{
isect->t = t;
isect->u = u;
isect->v = v;
isect->prim = prim;
isect->object = object;
isect->type = PRIMITIVE_MOTION_TRIANGLE;
return true;
}
}
return false;
}
/* Special ray intersection routines for local intersections. In that case we
* only want to intersect with primitives in the same object, and if case of
* multiple hits we pick a single random primitive as the intersection point.
* Returns whether traversal should be stopped.
*/
#ifdef __BVH_LOCAL__
ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg,
ccl_private LocalIntersection *local_isect,
const float3 P,
const float3 dir,
const float time,
const int object,
const int prim,
const float tmin,
const float tmax,
ccl_private uint *lcg_state,
const int max_hits)
{
/* Get vertex locations for intersection. */
float3 verts[3];
motion_triangle_vertices(kg, object, prim, time, verts);
/* Ray-triangle intersection, unoptimized. */
float t;
float u;
float v;
if (!ray_triangle_intersect(P, dir, tmin, tmax, verts[0], verts[1], verts[2], &u, &v, &t)) {
return false;
}
/* If no actual hit information is requested, just return here. */
if (max_hits == 0) {
return true;
}
const int hit_index = local_intersect_get_record_index(local_isect, t, lcg_state, max_hits);
if (hit_index == -1) {
return false;
}
/* Record intersection. */
ccl_private Intersection *isect = &local_isect->hits[hit_index];
isect->t = t;
isect->u = u;
isect->v = v;
isect->prim = prim;
isect->object = object;
isect->type = PRIMITIVE_MOTION_TRIANGLE;
/* Record geometric normal. */
local_isect->Ng[hit_index] = normalize(cross(verts[1] - verts[0], verts[2] - verts[0]));
return false;
}
#endif /* __BVH_LOCAL__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,73 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Motion Triangle Primitive
*
* These are stored as regular triangles, plus extra positions and normals at
* times other than the frame center. Computing the triangle vertex positions
* or normals at a given ray time is a matter of interpolation of the two steps
* between which the ray time lies.
*
* The extra positions are stored as additional motion steps in ATTR_STD_POSITION.
* Normals in ATTR_STD_VERTEX_NORMAL or ATTR_STD_CORNER_NORMAL.
*/
#pragma once
#include "kernel/globals.h"
#include "kernel/types.h"
#include "kernel/geom/motion_triangle.h"
#include "kernel/geom/motion_triangle_intersect.h"
#include "kernel/geom/triangle_intersect.h"
CCL_NAMESPACE_BEGIN
/* Setup of motion triangle specific parts of ShaderData, moved into this one
* function to more easily share computation of interpolated positions and
* normals */
/* return 3 triangle vertex normals */
ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, ccl_private ShaderData *sd)
{
/* Get shader. */
sd->shader = kernel_data_fetch(tri_shader, sd->prim);
/* Compute motion info. */
int numsteps;
int step;
float t;
uint3 tri_vindex;
motion_triangle_compute_info(
kg, sd->object, sd->time, sd->prim, &tri_vindex, &numsteps, &step, &t);
float3 verts[3];
const int numverts = kernel_data_fetch(objects, sd->object).numverts;
motion_triangle_vertices(kg, sd->object, tri_vindex, numsteps, numverts, step, t, verts);
/* Compute refined position. */
sd->P = triangle_point_from_uv_and_verts(kg, sd, sd->u, sd->v, verts);
/* Compute face normal. */
float3 Ng;
if (object_negative_scale_applied(sd->object_flag)) {
Ng = normalize(cross(verts[2] - verts[0], verts[1] - verts[0]));
}
else {
Ng = normalize(cross(verts[1] - verts[0], verts[2] - verts[0]));
}
sd->Ng = Ng;
sd->N = Ng;
/* Compute derivatives of P w.r.t. uv. */
#ifdef __DPDU__
sd->dPdu = (verts[1] - verts[0]);
sd->dPdv = (verts[2] - verts[0]);
#endif
/* Compute smooth normal. */
if (sd->shader & SHADER_SMOOTH_NORMAL) {
sd->N = motion_triangle_smooth_normal(
kg, Ng, sd->object, sd->prim, tri_vindex, numsteps, step, t, sd->u, sd->v);
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,512 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Object Primitive
*
* All mesh and curve primitives are part of an object. The same mesh and curves
* may be instanced multiple times by different objects.
*
* If the mesh is not instanced multiple times, the object will not be explicitly
* stored as a primitive in the BVH, rather the bare triangles are curved are
* directly primitives in the BVH with world space locations applied, and the object
* ID is looked up afterwards. */
#pragma once
#include "kernel/globals.h"
#include "kernel/types.h"
CCL_NAMESPACE_BEGIN
/* Object attributes, for now a fixed size and contents */
enum ObjectTransform {
OBJECT_TRANSFORM = 0,
OBJECT_INVERSE_TRANSFORM = 1,
};
enum ObjectVectorTransform { OBJECT_PASS_MOTION_PRE = 0, OBJECT_PASS_MOTION_POST = 1 };
/* Object to world space transformation */
ccl_device_inline Transform object_fetch_transform(KernelGlobals kg,
const int object,
enum ObjectTransform type)
{
if (type == OBJECT_INVERSE_TRANSFORM) {
return kernel_data_fetch(objects, object).itfm;
}
return kernel_data_fetch(objects, object).tfm;
}
/* Object to world space transformation for motion vectors */
ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals kg,
const int object,
enum ObjectVectorTransform type)
{
const int offset = object * OBJECT_MOTION_PASS_SIZE + (int)type;
return kernel_data_fetch(object_motion_pass, offset);
}
/* Motion blurred object transformations */
#ifdef __OBJECT_MOTION__
ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals kg,
const int object,
const float time)
{
const uint motion_offset = kernel_data_fetch(objects, object).motion_offset;
const ccl_global DecomposedTransform *motion = &kernel_data_fetch(object_motion, motion_offset);
const int num_steps = kernel_data_fetch(objects, object).num_tfm_steps;
Transform tfm;
transform_motion_array_interpolate(&tfm, motion, num_steps, time);
return tfm;
}
#endif /* __OBJECT_MOTION__ */
ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg,
const int object,
const float time,
ccl_private Transform *itfm)
{
#ifdef __OBJECT_MOTION__
const uint object_flag = kernel_data_fetch(object_flag, object);
if (object_flag & SD_OBJECT_MOTION) {
/* if we do motion blur */
Transform tfm = object_fetch_transform_motion(kg, object, time);
if (itfm) {
*itfm = transform_inverse(tfm);
}
return tfm;
}
#endif /* __OBJECT_MOTION__ */
Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM);
if (itfm) {
*itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
}
return tfm;
}
/* Get transform matrix for shading point. */
ccl_device_inline Transform object_get_transform(KernelGlobals kg,
const ccl_private ShaderData *sd)
{
#ifdef __OBJECT_MOTION__
return (sd->object_flag & SD_OBJECT_MOTION) ?
sd->ob_tfm_motion :
object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
#else
return object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
#endif
}
ccl_device_inline Transform object_get_inverse_transform(KernelGlobals kg,
const ccl_private ShaderData *sd)
{
#ifdef __OBJECT_MOTION__
return (sd->object_flag & SD_OBJECT_MOTION) ?
sd->ob_itfm_motion :
object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
#else
return object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
#endif
}
ccl_device_inline Transform lamp_get_inverse_transform(KernelGlobals kg,
const ccl_global KernelLight *klight)
{
return object_fetch_transform(kg, klight->object_id, OBJECT_INVERSE_TRANSFORM);
}
/* Transform position from object to world space */
template<class T>
ccl_device_inline void object_position_transform(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private T *P)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
*P = transform_point_auto(&sd->ob_tfm_motion, *P);
return;
}
#endif
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
*P = transform_point(&tfm, *P);
}
/* Transform position from world to object space */
template<class T>
ccl_device_inline void object_inverse_position_transform(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private T *P)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
*P = transform_point_auto(&sd->ob_itfm_motion, *P);
return;
}
#endif
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
*P = transform_point(&tfm, *P);
}
/* Convenience wrapper that checks for OBJECT_NONE before transforming.
* Works with both plain types (float3) and dual types (dual3). */
template<class Float3Type>
ccl_device_inline void object_inverse_position_transform_if_object(
KernelGlobals kg, const ccl_private ShaderData *sd, ccl_private Float3Type *P)
{
if (sd->object != OBJECT_NONE) {
object_inverse_position_transform(kg, sd, P);
}
}
/* Transform normal from world to object space */
ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private float3 *N)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
if (sd->object != OBJECT_NONE) {
*N = safe_normalize(transform_direction_transposed_auto(&sd->ob_tfm_motion, *N));
}
return;
}
#endif
if (sd->object != OBJECT_NONE) {
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
*N = safe_normalize(transform_direction_transposed(&tfm, *N));
}
}
/* Transform normal from object to world space */
template<class T>
ccl_device_inline void object_normal_transform(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private T *N)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
*N = normalize(transform_direction_transposed_auto(&sd->ob_itfm_motion, *N));
return;
}
#endif
if (sd->object != OBJECT_NONE) {
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
*N = normalize(transform_direction_transposed(&tfm, *N));
}
}
ccl_device_inline bool object_negative_scale_applied(const uint object_flag)
{
return ((object_flag & SD_OBJECT_NEGATIVE_SCALE) && (object_flag & SD_OBJECT_TRANSFORM_APPLIED));
}
/* Transform direction vector from object to world space */
ccl_device_inline void object_dir_transform(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private float3 *D)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
*D = transform_direction_auto(&sd->ob_tfm_motion, *D);
return;
}
#endif
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
*D = transform_direction(&tfm, *D);
}
/* Transform direction vector from world to object space */
ccl_device_inline void object_inverse_dir_transform(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private float3 *D)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
*D = transform_direction_auto(&sd->ob_itfm_motion, *D);
return;
}
#endif
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM);
*D = transform_direction(&tfm, *D);
}
/* Object center position */
ccl_device_inline float3 object_location(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if (sd->object == OBJECT_NONE) {
return make_float3(0.0f, 0.0f, 0.0f);
}
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
return make_float3(sd->ob_tfm_motion.x.w, sd->ob_tfm_motion.y.w, sd->ob_tfm_motion.z.w);
}
#endif
const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM);
return make_float3(tfm.x.w, tfm.y.w, tfm.z.w);
}
/* Color of the object */
ccl_device_inline float3 object_color(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return make_float3(0.0f, 0.0f, 0.0f);
}
const ccl_global KernelObject *kobject = &kernel_data_fetch(objects, object);
return make_float3(kobject->color[0], kobject->color[1], kobject->color[2]);
}
/* Alpha of the object */
ccl_device_inline float object_alpha(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 0.0f;
}
return kernel_data_fetch(objects, object).alpha;
}
/* Pass ID number of object */
ccl_device_inline float object_pass_id(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 0.0f;
}
return kernel_data_fetch(objects, object).pass_id;
}
/* Light-group of object. */
ccl_device_inline int object_lightgroup(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return LIGHTGROUP_NONE;
}
return kernel_data_fetch(objects, object).lightgroup;
}
/* Per object random number for shader variation */
ccl_device_inline float object_random_number(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 0.0f;
}
return kernel_data_fetch(objects, object).random_number;
}
/* Particle ID from which this object was generated */
ccl_device_inline int object_particle_id(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 0;
}
return kernel_data_fetch(objects, object).particle_index;
}
/* Generated texture coordinate on surface from where object was instanced */
ccl_device_inline float3 object_dupli_generated(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return make_float3(0.0f, 0.0f, 0.0f);
}
const ccl_global KernelObject *kobject = &kernel_data_fetch(objects, object);
return make_float3(
kobject->dupli_generated[0], kobject->dupli_generated[1], kobject->dupli_generated[2]);
}
/* UV texture coordinate on surface from where object was instanced */
ccl_device_inline float3 object_dupli_uv(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return make_float3(0.0f, 0.0f, 0.0f);
}
const ccl_global KernelObject *kobject = &kernel_data_fetch(objects, object);
return make_float3(kobject->dupli_uv[0], kobject->dupli_uv[1], 0.0f);
}
/* Volume density */
ccl_device_inline float object_volume_density(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 1.0f;
}
return kernel_data_fetch(objects, object).volume_density;
}
/* Pass ID for shader */
ccl_device int shader_pass_id(KernelGlobals kg, const ccl_private ShaderData *sd)
{
return kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).pass_id;
}
/* Cryptomatte ID */
ccl_device_inline float object_cryptomatte_id(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 0.0f;
}
return kernel_data_fetch(objects, object).cryptomatte_object;
}
ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals kg, const int object)
{
if (object == OBJECT_NONE) {
return 0;
}
return kernel_data_fetch(objects, object).cryptomatte_asset;
}
/* Particle data from which object was instanced */
ccl_device_inline uint particle_index(KernelGlobals kg, const int particle)
{
return kernel_data_fetch(particles, particle).index;
}
ccl_device float particle_age(KernelGlobals kg, const int particle)
{
return kernel_data_fetch(particles, particle).age;
}
ccl_device float particle_lifetime(KernelGlobals kg, const int particle)
{
return kernel_data_fetch(particles, particle).lifetime;
}
ccl_device float particle_size(KernelGlobals kg, const int particle)
{
return kernel_data_fetch(particles, particle).size;
}
ccl_device float4 particle_rotation(KernelGlobals kg, const int particle)
{
return kernel_data_fetch(particles, particle).rotation;
}
ccl_device float3 particle_location(KernelGlobals kg, const int particle)
{
return make_float3(kernel_data_fetch(particles, particle).location);
}
ccl_device float3 particle_velocity(KernelGlobals kg, const int particle)
{
return make_float3(kernel_data_fetch(particles, particle).velocity);
}
ccl_device float3 particle_angular_velocity(KernelGlobals kg, const int particle)
{
return make_float3(kernel_data_fetch(particles, particle).angular_velocity);
}
/* Object intersection in BVH */
ccl_device_inline float3 bvh_clamp_direction(const float3 dir)
{
const float ooeps = 8.271806E-25f;
return make_float3((fabsf(dir.x) > ooeps) ? dir.x : copysignf(ooeps, dir.x),
(fabsf(dir.y) > ooeps) ? dir.y : copysignf(ooeps, dir.y),
(fabsf(dir.z) > ooeps) ? dir.z : copysignf(ooeps, dir.z));
}
ccl_device_inline float3 bvh_inverse_direction(const float3 dir)
{
return reciprocal(dir);
}
/* Transform ray into object space to enter static object in BVH */
ccl_device_inline void bvh_instance_push(KernelGlobals kg,
const int object,
const ccl_private Ray *ray,
ccl_private float3 *P,
ccl_private float3 *dir,
ccl_private float3 *idir)
{
const Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM);
*P = transform_point(&tfm, ray->P);
*dir = bvh_clamp_direction(transform_direction(&tfm, ray->D));
*idir = bvh_inverse_direction(*dir);
}
#ifdef __OBJECT_MOTION__
/* Transform ray into object space to enter motion blurred object in BVH */
ccl_device_inline void bvh_instance_motion_push(KernelGlobals kg,
const int object,
const ccl_private Ray *ray,
ccl_private float3 *P,
ccl_private float3 *dir,
ccl_private float3 *idir)
{
Transform tfm;
object_fetch_transform_motion_test(kg, object, ray->time, &tfm);
*P = transform_point(&tfm, ray->P);
*dir = bvh_clamp_direction(transform_direction(&tfm, ray->D));
*idir = bvh_inverse_direction(*dir);
}
#endif
/* Transform ray to exit static object in BVH. */
ccl_device_inline void bvh_instance_pop(const ccl_private Ray *ray,
ccl_private float3 *P,
ccl_private float3 *dir,
ccl_private float3 *idir)
{
*P = ray->P;
*dir = bvh_clamp_direction(ray->D);
*idir = bvh_inverse_direction(*dir);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,100 @@
/* SPDX-FileCopyrightText: 2021-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/motion_point.h"
#include "kernel/geom/object.h"
CCL_NAMESPACE_BEGIN
/* Point Primitive
*
* Point primitive for rendering point clouds.
*/
#ifdef __POINTCLOUD__
/* Reading attributes on various point elements */
template<typename T>
ccl_device T point_attribute(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc)
{
if (desc.element & ATTR_ELEMENT_VERTEX) {
return T(attribute_data_fetch<dual_base_t<T>>(kg, desc.element, desc.offset + sd->prim));
}
return make_zero<T>();
}
/* Point position */
ccl_device float3 point_position(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if (sd->type & PRIMITIVE_POINT) {
/* World space center. */
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
float3 P = (sd->type & PRIMITIVE_MOTION) ?
make_float3(motion_point(kg, sd->object, sd->prim, sd->time)) :
make_float3(kernel_data_fetch(points, position_offset + sd->prim));
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &P);
}
return P;
}
return zero_float3();
}
/* Point radius */
ccl_device float point_radius(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if (sd->type & PRIMITIVE_POINT) {
/* World space radius. */
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
const float r = kernel_data_fetch(points, position_offset + sd->prim).w;
if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) {
return r;
}
const float normalized_r = r * (1.0f / M_SQRT3_F);
float3 dir = make_float3(normalized_r, normalized_r, normalized_r);
object_dir_transform(kg, sd, &dir);
return len(dir);
}
return 0.0f;
}
/* Point random */
ccl_device float point_random(KernelGlobals kg, const ccl_private ShaderData *sd)
{
if (sd->type & PRIMITIVE_POINT) {
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POINT_RANDOM);
return is_attribute_found(desc) ? point_attribute<float>(kg, sd, desc) : 0.0f;
}
return 0.0f;
}
/* Point location for motion pass, linear interpolation between keys and
* ignoring radius because we do the same for the motion keys */
ccl_device float3 point_motion_center_location(KernelGlobals kg, const ccl_private ShaderData *sd)
{
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
return make_float3(kernel_data_fetch(points, position_offset + sd->prim));
}
#endif /* __POINTCLOUD__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,130 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/types.h"
#include "kernel/geom/motion_point.h"
#include "kernel/geom/object.h"
CCL_NAMESPACE_BEGIN
/* Point primitive intersection functions. */
#ifdef __POINTCLOUD__
ccl_device_forceinline bool point_intersect_test(const float4 point,
const float3 ray_P,
const float3 ray_D,
const float ray_tmin,
const float ray_tmax,
ccl_private float *t)
{
const float3 center = make_float3(point);
const float radius = point.w;
const float rd2 = 1.0f / dot(ray_D, ray_D);
const float3 c0 = center - ray_P;
const float projC0 = dot(c0, ray_D) * rd2;
const float3 perp = c0 - projC0 * ray_D;
const float l2 = dot(perp, perp);
const float r2 = radius * radius;
if (!(l2 <= r2)) {
return false;
}
const float td = sqrt((r2 - l2) * rd2);
const float t_front = projC0 - td;
const bool valid_front = (ray_tmin <= t_front) & (t_front <= ray_tmax);
/* Always back-face culling for now. */
# if 0
const float t_back = projC0 + td;
const bool valid_back = (ray_tmin <= t_back) & (t_back <= ray_tmax);
/* check if there is a first hit */
const bool valid_first = valid_front | valid_back;
if (!valid_first) {
return false;
}
*t = (valid_front) ? t_front : t_back;
return true;
# else
if (!valid_front) {
return false;
}
*t = t_front;
return true;
# endif
}
ccl_device_forceinline bool point_intersect(KernelGlobals kg,
ccl_private Intersection *isect,
const float3 ray_P,
const float3 ray_D,
const float ray_tmin,
const float ray_tmax,
const int object,
const int prim,
const float time,
const int type)
{
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const float4 point = (type & PRIMITIVE_MOTION) ?
motion_point(kg, object, prim, time) :
kernel_data_fetch(points, position_offset + prim);
if (!point_intersect_test(point, ray_P, ray_D, ray_tmin, ray_tmax, &isect->t)) {
return false;
}
isect->prim = prim;
isect->object = object;
isect->type = type;
isect->u = 0.0f;
isect->v = 0.0f;
return true;
}
ccl_device_inline void point_shader_setup(KernelGlobals kg,
ccl_private ShaderData *sd,
const ccl_private Intersection *isect,
const ccl_private Ray *ray)
{
sd->shader = kernel_data_fetch(points_shader, isect->prim);
sd->P = ray->P + ray->D * isect->t;
/* Texture coordinates, zero for now. */
# ifdef __UV__
sd->u = isect->u;
sd->v = isect->v;
# endif
/* Compute point center for normal. */
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
float3 center = make_float3((isect->type & PRIMITIVE_MOTION) ?
motion_point(kg, sd->object, sd->prim, sd->time) :
kernel_data_fetch(points, position_offset + sd->prim));
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &center);
}
/* Normal */
sd->Ng = normalize(sd->P - center);
sd->N = sd->Ng;
# ifdef __DPDU__
/* dPdu/dPdv */
sd->dPdu = make_float3(0.0f, 0.0f, 0.0f);
sd->dPdv = make_float3(0.0f, 0.0f, 0.0f);
# endif
}
#endif
CCL_NAMESPACE_END

View File

@@ -0,0 +1,373 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Primitive Utilities
*
* Generic functions to look up mesh, curve and volume primitive attributes for
* shading and render passes. */
#pragma once
#include "kernel/globals.h"
#include "kernel/camera/projection.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/curve.h"
#include "kernel/geom/object.h"
#include "kernel/geom/point.h"
#include "kernel/geom/triangle.h"
#include "kernel/geom/volume.h"
CCL_NAMESPACE_BEGIN
/* Surface Attributes
*
* Read geometry attributes for surface shading. This is distinct from volume
* attributes for performance, mainly for GPU performance to avoid bringing in
* heavy volume interpolation code. */
template<typename T>
ccl_device_forceinline T primitive_surface_attribute(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc)
{
using BaseT = dual_base_t<T>;
if (desc.element & (ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
return T(attribute_data_fetch<BaseT>(kg, desc.element, desc.offset));
}
if (sd->type & PRIMITIVE_TRIANGLE) {
return triangle_attribute<T>(kg, sd, desc);
}
#ifdef __HAIR__
if (sd->type & PRIMITIVE_CURVE) {
return curve_attribute<T>(kg, sd, desc);
}
#endif
#ifdef __POINTCLOUD__
else if (sd->type & PRIMITIVE_POINT) {
return point_attribute<T>(kg, sd, desc);
}
#endif
else {
return make_zero<T>();
}
}
/* Set sd->N to the undisplaced normal. For smooth shading, use the stored undisplaced
* normal attribute. For flat shading, compute the geometric face normal from undisplaced
* triangle positions. */
ccl_device void primitive_normal_set_undisplaced(KernelGlobals kg,
ccl_private ShaderData *sd,
const int position_undisplaced_offset)
{
float3 N;
if (sd->shader & SHADER_SMOOTH_NORMAL) {
const AttributeDescriptor ndesc = find_attribute(kg, sd, ATTR_STD_NORMAL_UNDISPLACED);
if (!is_attribute_found(ndesc)) {
return;
}
N = safe_normalize(primitive_surface_attribute<float3>(kg, sd, ndesc));
}
else {
N = triangle_face_normal_undisplaced(kg, sd, position_undisplaced_offset);
}
object_normal_transform(kg, sd, &N);
sd->N = (sd->flag & SD_BACKFACING) ? -N : N;
}
#ifdef __VOLUME__
/* Volume Attributes
*
* Read geometry attributes for volume shading. This is distinct from surface
* attributes for performance, mainly for GPU performance to avoid bringing in
* heavy volume interpolation code. */
ccl_device_forceinline bool primitive_is_volume_attribute(const ccl_private ShaderData *sd)
{
return sd->type == PRIMITIVE_VOLUME;
}
template<typename T>
ccl_device_inline T primitive_volume_attribute(KernelGlobals kg,
ccl_private ShaderData *sd,
const AttributeDescriptor desc,
const bool stochastic)
{
if (primitive_is_volume_attribute(sd)) {
return volume_attribute_value<T>(volume_attribute_float4(kg, sd, desc, stochastic));
}
return make_zero<T>();
}
#endif
/* Default UV coordinate */
ccl_device_forceinline float3 primitive_uv(KernelGlobals kg, const ccl_private ShaderData *sd)
{
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV);
if (!is_attribute_found(desc)) {
return make_float3(0.0f, 0.0f, 0.0f);
}
const float2 uv = primitive_surface_attribute<float2>(kg, sd, desc);
return make_float3(uv.x, uv.y, 1.0f);
}
/* PTEX coordinates. */
ccl_device bool primitive_ptex(KernelGlobals kg,
ccl_private ShaderData *sd,
ccl_private float2 *uv,
ccl_private int *face_id)
{
/* storing ptex data as attributes is not memory efficient but simple for tests */
const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID);
const AttributeDescriptor desc_uv = find_attribute(kg, sd, ATTR_STD_PTEX_UV);
if (!is_attribute_found(desc_face_id) || !is_attribute_found(desc_uv)) {
return false;
}
const float3 uv3 = primitive_surface_attribute<float3>(kg, sd, desc_uv);
const float face_id_f = primitive_surface_attribute<float>(kg, sd, desc_face_id);
*uv = make_float2(uv3.x, uv3.y);
*face_id = (int)face_id_f;
return true;
}
/* Surface tangent */
template<typename Float3Type>
ccl_device Float3Type primitive_tangent(KernelGlobals kg, ccl_private ShaderData *sd)
{
#if defined(__HAIR__) || defined(__POINTCLOUD__)
if (sd->type & (PRIMITIVE_CURVE | PRIMITIVE_POINT)) {
# ifdef __DPDU__
return Float3Type(normalize(sd->dPdu));
}
# else
return make_zero<Float3Type>();
# endif
#endif
/* try to create spherical tangent from generated coordinates */
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED);
if (is_attribute_found(desc)) {
if constexpr (is_dual_v<Float3Type>) {
dual3 data = primitive_surface_attribute<dual3>(kg, sd, desc);
data = make_float3(-(data.y() - 0.5f), (data.x() - 0.5f), dual1());
object_normal_transform(kg, sd, &data);
return cross(sd->N, normalize(cross(data, sd->N)));
}
else {
float3 data = primitive_surface_attribute<float3>(kg, sd, desc);
data = make_float3(-(data.y - 0.5f), (data.x - 0.5f), 0.0f);
object_normal_transform(kg, sd, &data);
return cross(sd->N, normalize(cross(data, sd->N)));
}
}
/* otherwise use surface derivatives */
#ifdef __DPDU__
return Float3Type(normalize(sd->dPdu));
#else
return make_zero<Float3Type>();
#endif
}
/* Motion vector common */
ccl_device_inline float3 primitive_motion_position(KernelGlobals kg,
const ccl_private ShaderData *sd,
const int offset)
{
#if defined(__HAIR__)
if (sd->type & PRIMITIVE_CURVE) {
const KernelCurve curve = kernel_data_fetch(curves, sd->prim);
const int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
const int k1 = k0 + 1;
const float4 f0 = kernel_data_fetch(curve_keys, offset + k0);
const float4 f1 = kernel_data_fetch(curve_keys, offset + k1);
return make_float3(mix(f0, f1, sd->u));
}
#endif
#if defined(__POINTCLOUD__)
if (sd->type & PRIMITIVE_POINT) {
return make_float3(kernel_data_fetch(points, offset + sd->prim));
}
#endif
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
const float3 v0 = kernel_data_fetch(tri_verts, offset + tri_vindex.x);
const float3 v1 = kernel_data_fetch(tri_verts, offset + tri_vindex.y);
const float3 v2 = kernel_data_fetch(tri_verts, offset + tri_vindex.z);
return triangle_interpolate(sd->u, sd->v, v0, v1, v2);
}
ccl_device_forceinline void primitive_motion_data_without_camera(KernelGlobals kg,
const ccl_private ShaderData *sd,
ccl_private float3 *motion_center,
ccl_private float3 *motion_pre,
ccl_private float3 *motion_post)
{
#if defined(__HAIR__) || defined(__POINTCLOUD__)
const bool is_curve_or_point = sd->type & (PRIMITIVE_CURVE | PRIMITIVE_POINT);
if (is_curve_or_point) {
*motion_center = make_float3(0.0f, 0.0f, 0.0f);
if (sd->type & PRIMITIVE_CURVE) {
# if defined(__HAIR__)
*motion_center = curve_motion_center_location(kg, sd);
# endif
}
else if (sd->type & PRIMITIVE_POINT) {
# if defined(__POINTCLOUD__)
*motion_center = point_motion_center_location(kg, sd);
# endif
}
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, motion_center);
}
}
else
#endif
{
*motion_center = sd->P;
}
*motion_pre = *motion_center;
*motion_post = *motion_center;
/* deformation motion */
const ccl_global KernelObject &kobject = kernel_data_fetch(objects, sd->object);
const int pos_offset = kobject.position_offset;
const int numverts = kobject.numverts;
const int num_motion_steps = kobject.num_geom_steps;
if (sd->object_flag & SD_OBJECT_HAS_VERTEX_MOTION) {
/* Motion steps are stored after the center position in the dedicated position arrays. */
int offset = pos_offset + numverts;
*motion_pre = primitive_motion_position(kg, sd, offset);
if (num_motion_steps > 2) {
offset += numverts;
*motion_post = primitive_motion_position(kg, sd, offset);
}
else {
object_inverse_position_transform(kg, sd, motion_post);
}
}
/* object motion. note that depending on the mesh having motion vectors, this
* transformation was set match the world/object space of motion_pre/post */
Transform tfm;
tfm = object_fetch_motion_pass_transform(kg, sd->object, OBJECT_PASS_MOTION_PRE);
*motion_pre = transform_point(&tfm, *motion_pre);
tfm = object_fetch_motion_pass_transform(kg, sd->object, OBJECT_PASS_MOTION_POST);
*motion_post = transform_point(&tfm, *motion_post);
}
ccl_device_forceinline void primitive_motion_data_camera_step(KernelGlobals kg,
ccl_private float3 *motion_center,
ccl_private float3 *motion_pre,
ccl_private float3 *motion_post)
{
Transform tfm;
/* camera motion, for perspective/orthographic motion.pre/post will be a
* world-to-raster matrix, for panorama it's world-to-camera, for custom
* we fall back to the world position until we have inverse mapping for it */
if (kernel_data.cam.type == CAMERA_CUSTOM) {
/* TODO: Custom cameras don't have inverse mappings yet, so we fall back to
* camera-space vectors here for now. */
tfm = kernel_data.cam.worldtocamera;
*motion_center = normalize(transform_point(&tfm, *motion_center));
tfm = kernel_data.cam.motion_pass_pre;
*motion_pre = normalize(transform_point(&tfm, *motion_pre));
tfm = kernel_data.cam.motion_pass_post;
*motion_post = normalize(transform_point(&tfm, *motion_post));
}
else if (kernel_data.cam.type != CAMERA_PANORAMA) {
/* Perspective and orthographics camera use the world-to-raster matrix. */
ProjectionTransform projection = kernel_data.cam.worldtoraster;
*motion_center = transform_perspective(&projection, *motion_center);
projection = kernel_data.cam.perspective_pre;
*motion_pre = transform_perspective(&projection, *motion_pre);
projection = kernel_data.cam.perspective_post;
*motion_post = transform_perspective(&projection, *motion_post);
}
else {
/* Panorama cameras have their own inverse mappings. */
tfm = kernel_data.cam.worldtocamera;
*motion_center = normalize(transform_point(&tfm, *motion_center));
*motion_center = make_float3(direction_to_panorama(&kernel_data.cam, *motion_center));
motion_center->x *= kernel_data.cam.width;
motion_center->y *= kernel_data.cam.height;
tfm = kernel_data.cam.motion_pass_pre;
*motion_pre = normalize(transform_point(&tfm, *motion_pre));
*motion_pre = make_float3(direction_to_panorama(&kernel_data.cam, *motion_pre));
motion_pre->x *= kernel_data.cam.width;
motion_pre->y *= kernel_data.cam.height;
tfm = kernel_data.cam.motion_pass_post;
*motion_post = normalize(transform_point(&tfm, *motion_post));
*motion_post = make_float3(direction_to_panorama(&kernel_data.cam, *motion_post));
motion_post->x *= kernel_data.cam.width;
motion_post->y *= kernel_data.cam.height;
}
}
/* Motion vector for motion pass */
ccl_device_forceinline float4 primitive_motion_vector(KernelGlobals kg,
const ccl_private ShaderData *sd)
{
float3 motion_center, motion_pre, motion_post;
primitive_motion_data_without_camera(kg, sd, &motion_center, &motion_pre, &motion_post);
primitive_motion_data_camera_step(kg, &motion_center, &motion_pre, &motion_post);
motion_pre = motion_pre - motion_center;
motion_post = motion_center - motion_post;
return make_float4(motion_pre.x, motion_pre.y, motion_post.x, motion_post.y);
}
/* Motion vector for denoising backward motion pass */
ccl_device_forceinline float3
primitive_motion_vector_backward_depth_delta(KernelGlobals kg, const ccl_private ShaderData *sd)
{
Transform tfm;
float3 motion_center, motion_pre, motion_post;
primitive_motion_data_without_camera(kg, sd, &motion_center, &motion_pre, &motion_post);
/* Get camera-space vectors for linear depth delta. */
tfm = kernel_data.cam.worldtocamera;
float3 motion_center_cam = transform_point(&tfm, motion_center);
tfm = kernel_data.cam.motion_pass_pre;
float3 motion_pre_cam = transform_point(&tfm, motion_pre);
primitive_motion_data_camera_step(kg, &motion_center, &motion_pre, &motion_post);
motion_pre = motion_pre - motion_center;
float linear_depth_delta_pre = motion_pre_cam.z - motion_center_cam.z;
return make_float3(motion_pre.x, motion_pre.y, linear_depth_delta_pre);
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,476 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Functions to initialize ShaderData given.
*
* Could be from an incoming ray, intersection or sampled position. */
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/curve_intersect.h"
#include "kernel/geom/motion_triangle_shader.h"
#include "kernel/geom/object.h"
#include "kernel/geom/point_intersect.h"
#include "kernel/geom/triangle_intersect.h"
#include "kernel/util/differential.h"
CCL_NAMESPACE_BEGIN
/* ShaderData setup from incoming ray */
ccl_device void shader_setup_object_transforms(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
const float time)
{
#ifdef __OBJECT_MOTION__
if (sd->object_flag & SD_OBJECT_MOTION) {
sd->ob_tfm_motion = object_fetch_transform_motion(kg, sd->object, time);
sd->ob_itfm_motion = transform_inverse(sd->ob_tfm_motion);
}
#endif
}
/* TODO: break this up if it helps reduce register pressure to load data from
* global memory as we write it to shader-data.
*
* HIP on Linux currently needs noinline to sidestep a probable compiler bug. */
#ifdef __KERNEL_HIP__
ccl_device_noinline
#else
ccl_device_inline
#endif
void
shader_setup_from_ray(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
const ccl_private Ray *ccl_restrict ray,
const ccl_private Intersection *ccl_restrict isect)
{
/* Read intersection data into shader globals.
*
* TODO: this is redundant, could potentially remove some of this from
* ShaderData but would need to ensure that it also works for shadow
* shader evaluation. */
sd->u = isect->u;
sd->v = isect->v;
sd->ray_length = isect->t;
sd->type = isect->type;
sd->object = isect->object;
sd->object_flag = kernel_data_fetch(object_flag, sd->object);
sd->prim = isect->prim;
sd->flag = 0;
/* Read matrices and time. */
sd->time = ray->time;
#ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, ray->time);
#endif
/* Read ray data into shader globals. */
sd->wi = -ray->D;
#ifdef __HAIR__
if (sd->type & PRIMITIVE_CURVE) {
/* curve */
curve_shader_setup(kg, sd, ray->P, ray->D, isect->t, isect->prim);
}
else
#endif
#ifdef __POINTCLOUD__
if (sd->type & PRIMITIVE_POINT)
{
/* point */
point_shader_setup(kg, sd, isect, ray);
}
else
#endif
{
if (sd->type == PRIMITIVE_TRIANGLE) {
/* static triangle */
triangle_shader_setup(kg, sd);
}
else {
kernel_assert(sd->type == PRIMITIVE_MOTION_TRIANGLE);
/* motion triangle */
motion_triangle_shader_setup(kg, sd);
}
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
/* instance transform */
object_normal_transform(kg, sd, &sd->N);
object_normal_transform(kg, sd, &sd->Ng);
#ifdef __DPDU__
object_dir_transform(kg, sd, &sd->dPdu);
object_dir_transform(kg, sd, &sd->dPdv);
#endif
}
}
sd->flag = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags;
/* backfacing test */
const bool backfacing = (dot(sd->Ng, sd->wi) < 0.0f);
if (backfacing) {
sd->flag |= SD_BACKFACING;
sd->Ng = -sd->Ng;
sd->N = -sd->N;
#ifdef __DPDU__
sd->dPdu = -sd->dPdu;
sd->dPdv = -sd->dPdv;
#endif
}
#ifdef __RAY_DIFFERENTIALS__
/* differentials */
sd->dP = differential_transfer_compact(ray->dP, ray->D, ray->dD, sd->ray_length);
sd->dI = differential_incoming_compact(ray->dD);
differential_dudv_compact(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng);
#endif
}
/* ShaderData setup from position sampled on mesh */
ccl_device_inline void shader_setup_from_sample(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
const float3 P,
const float3 Ng,
const float3 I,
const int shader,
const int object,
const int prim,
const float u,
const float v,
const float t,
const float time,
const bool object_space,
const bool is_lamp)
{
/* vectors */
sd->P = P;
sd->N = Ng;
sd->Ng = Ng;
sd->wi = I;
sd->shader = shader;
if (is_lamp) {
sd->type = PRIMITIVE_LAMP;
}
else if (prim != PRIM_NONE) {
sd->type = PRIMITIVE_TRIANGLE;
}
else {
sd->type = PRIMITIVE_NONE;
}
/* primitive */
sd->object = object;
/* Currently no access to bvh prim index for strand sd->prim. */
sd->prim = prim;
sd->u = u;
sd->v = v;
sd->time = time;
sd->ray_length = t;
sd->flag = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags;
sd->object_flag = 0;
if (sd->object != OBJECT_NONE) {
sd->object_flag |= kernel_data_fetch(object_flag, sd->object);
#ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, time);
#endif
/* transform into world space */
if (object_space) {
object_position_transform(kg, sd, &sd->P);
object_normal_transform(kg, sd, &sd->Ng);
sd->N = sd->Ng;
object_dir_transform(kg, sd, &sd->wi);
}
if (sd->type == PRIMITIVE_TRIANGLE) {
/* smooth normal */
if (sd->shader & SHADER_SMOOTH_NORMAL) {
sd->N = triangle_smooth_normal(
kg, Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v);
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_normal_transform(kg, sd, &sd->N);
}
}
/* dPdu/dPdv */
#ifdef __DPDU__
triangle_dPdudv(kg, sd->object, sd->prim, &sd->dPdu, &sd->dPdv);
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_dir_transform(kg, sd, &sd->dPdu);
object_dir_transform(kg, sd, &sd->dPdv);
}
#endif
}
else {
#ifdef __DPDU__
sd->dPdu = zero_float3();
sd->dPdv = zero_float3();
#endif
}
}
else {
#ifdef __DPDU__
sd->dPdu = zero_float3();
sd->dPdv = zero_float3();
#endif
}
/* backfacing test */
if (sd->prim != PRIM_NONE) {
const bool backfacing = (dot(sd->Ng, sd->wi) < 0.0f);
if (backfacing) {
sd->flag |= SD_BACKFACING;
sd->Ng = -sd->Ng;
sd->N = -sd->N;
#ifdef __DPDU__
sd->dPdu = -sd->dPdu;
sd->dPdv = -sd->dPdv;
#endif
}
}
#ifdef __RAY_DIFFERENTIALS__
/* no ray differentials here yet */
sd->dP = differential_zero_compact();
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
#endif
}
/* ShaderData setup for displacement */
ccl_device void shader_setup_from_displace(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
const int object,
const int prim,
const float u,
const float v)
{
float3 P;
float3 Ng;
const float3 I = zero_float3();
int shader;
triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader);
/* force smooth shading for displacement */
shader |= SHADER_SMOOTH_NORMAL;
shader_setup_from_sample(kg,
sd,
P,
Ng,
I,
shader,
object,
prim,
u,
v,
0.0f,
0.5f,
!(kernel_data_fetch(object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED),
false);
/* Assign some incoming direction to avoid division by zero. */
sd->wi = sd->N;
#ifdef __RAY_DIFFERENTIALS__
/* Set ray differentials based on triangle size for texture filtering.
* The parametric step across the triangle is 1.0, giving dPdx = dPdu
* and dPdy = dPdv.
* TODO: consider computing this based on all triangles adjacent to the vertex. */
sd->du.dx = 1.0f;
sd->du.dy = 0.0f;
sd->dv.dx = 0.0f;
sd->dv.dy = 1.0f;
sd->dP = 0.5f * (len(sd->dPdu) + len(sd->dPdv));
#endif
}
/* ShaderData setup for point on curve. */
#ifdef __HAIR__
ccl_device void shader_setup_from_curve(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
const int object,
const int prim,
const int segment,
const float u)
{
/* Primitive */
sd->type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE_THICK, segment);
sd->prim = prim;
sd->u = u;
sd->v = 0.0f;
sd->time = 0.5f;
sd->ray_length = 0.0f;
/* Shader */
sd->shader = kernel_data_fetch(curves, prim).shader_id;
sd->flag = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags;
/* Object */
sd->object = object;
sd->object_flag = kernel_data_fetch(object_flag, sd->object);
# ifdef __OBJECT_MOTION__
shader_setup_object_transforms(kg, sd, sd->time);
# endif
/* Get control points. */
const KernelCurve kcurve = kernel_data_fetch(curves, prim);
const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type);
const int k1 = k0 + 1;
const int ka = max(k0 - 1, kcurve.first_key);
const int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1);
float4 P_curve[4];
const int position_offset = kernel_data_fetch(objects, object).position_offset;
P_curve[0] = kernel_data_fetch(curve_keys, position_offset + ka);
P_curve[1] = kernel_data_fetch(curve_keys, position_offset + k0);
P_curve[2] = kernel_data_fetch(curve_keys, position_offset + k1);
P_curve[3] = kernel_data_fetch(curve_keys, position_offset + kb);
/* Interpolate position and tangent. */
sd->P = (sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_THICK_LINEAR ?
make_float3(linear_basis_eval(P_curve, sd->u)) :
make_float3(catmull_rom_basis_eval(P_curve, sd->u));
# ifdef __DPDU__
sd->dPdu = (sd->type & PRIMITIVE_CURVE) == PRIMITIVE_CURVE_THICK_LINEAR ?
make_float3(linear_basis_derivative(P_curve, sd->u)) :
make_float3(catmull_rom_basis_derivative(P_curve, sd->u));
# endif
/* Transform into world space */
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
object_position_transform(kg, sd, &sd->P);
# ifdef __DPDU__
object_dir_transform(kg, sd, &sd->dPdu);
# endif
}
/* Pick arbitrary view direction, normals and bitangent to avoid NaNs elsewhere. */
sd->wi = normalize(cross(make_float3(0, 1, 0), sd->dPdu));
sd->N = sd->wi;
sd->Ng = sd->wi;
# ifdef __DPDU__
sd->dPdv = cross(sd->dPdu, sd->Ng);
# endif
/* No ray differentials currently. */
# ifdef __RAY_DIFFERENTIALS__
sd->dP = differential_zero_compact();
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
# endif
}
#endif /* __HAIR__ */
/* ShaderData setup from ray into background */
ccl_device_inline void shader_setup_from_background(KernelGlobals kg,
ccl_private ShaderData *ccl_restrict sd,
const float3 ray_P,
const float3 ray_D,
const float ray_dD,
const float ray_time)
{
/* for NDC coordinates */
sd->ray_P = ray_P;
/* vectors */
sd->P = ray_D;
sd->N = -ray_D;
sd->Ng = -ray_D;
sd->wi = -ray_D;
sd->shader = kernel_data.background.surface_shader;
sd->flag = kernel_data_fetch(shaders, (sd->shader & SHADER_MASK)).flags;
sd->object_flag = 0;
sd->time = ray_time;
sd->ray_length = FLT_MAX;
sd->object = OBJECT_NONE;
sd->prim = PRIM_NONE;
sd->type = PRIMITIVE_NONE;
sd->u = 0.0f;
sd->v = 0.0f;
#ifdef __DPDU__
/* dPdu/dPdv */
/* Construct arbitrary local coordinate system. */
make_orthonormals(sd->Ng, &sd->dPdu, &sd->dPdv);
#endif
#ifdef __RAY_DIFFERENTIALS__
/* differentials */
sd->dP = ray_dD;
sd->dI = differential_incoming_compact(ray_dD);
/* Make the uv coordinate system match the constructed local coordinate system. */
sd->du.dx = sd->dv.dy = sd->dP;
sd->du.dy = sd->dv.dx = 0.0f;
#endif
}
/* ShaderData setup from point inside volume */
#ifdef __VOLUME__
ccl_device_inline void shader_setup_from_volume(ccl_private ShaderData *ccl_restrict sd,
const ccl_private Ray *ccl_restrict ray,
const int object)
{
/* vectors */
sd->P = ray->P + ray->D * ray->tmin;
sd->N = -ray->D;
sd->Ng = -ray->D;
sd->wi = -ray->D;
sd->shader = SHADER_NONE;
sd->flag = 0;
sd->object_flag = 0;
sd->time = ray->time;
sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */
/* TODO: fill relevant fields for texture coordinates. */
sd->object = object;
sd->prim = PRIM_NONE;
sd->type = PRIMITIVE_VOLUME;
sd->u = 0.0f;
sd->v = 0.0f;
# ifdef __DPDU__
/* dPdu/dPdv */
sd->dPdu = zero_float3();
sd->dPdv = zero_float3();
# endif
# ifdef __RAY_DIFFERENTIALS__
/* differentials */
sd->dP = differential_zero_compact(); /* TODO ray->dD */
sd->dI = differential_zero_compact();
sd->du = differential_zero();
sd->dv = differential_zero();
# endif
/* for NDC coordinates */
sd->ray_P = ray->P;
}
#endif /* __VOLUME__ */
CCL_NAMESPACE_END

View File

@@ -0,0 +1,340 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Triangle Primitive
*
* Basic triangle with 3 vertices is used to represent mesh surfaces. For BVH
* ray intersection we use a precomputed triangle storage to accelerate
* intersection at the cost of more memory usage */
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/object.h"
CCL_NAMESPACE_BEGIN
/* Evaluate a quantity at barycentric coordinates u, v, given the values at three triangle
* vertices. */
template<typename T>
ccl_device_inline T
triangle_interpolate(const float u, const float v, const T f0, const T f1, const T f2)
{
return (1.0f - u - v) * f0 + u * f1 + v * f2;
}
/* Normal on triangle. */
ccl_device_inline float3 triangle_normal(KernelGlobals kg, ccl_private ShaderData *sd)
{
/* load triangle vertices */
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
const float3 v0 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 v1 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 v2 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
/* return normal */
if (object_negative_scale_applied(sd->object_flag)) {
return normalize(cross(v2 - v0, v1 - v0));
}
return normalize(cross(v1 - v0, v2 - v0));
}
/* Face normal of undisplaced triangle, from vertex positions stored as attribute. */
ccl_device_inline float3 triangle_face_normal_undisplaced(KernelGlobals kg,
ccl_private const ShaderData *sd,
const int position_attr_offset)
{
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
const float3 v0 = attribute_data_fetch<float3>(
kg, ATTR_ELEMENT_VERTEX, position_attr_offset + tri_vindex.x);
const float3 v1 = attribute_data_fetch<float3>(
kg, ATTR_ELEMENT_VERTEX, position_attr_offset + tri_vindex.y);
const float3 v2 = attribute_data_fetch<float3>(
kg, ATTR_ELEMENT_VERTEX, position_attr_offset + tri_vindex.z);
if (object_negative_scale_applied(sd->object_flag)) {
return normalize(cross(v2 - v0, v1 - v0));
}
return normalize(cross(v1 - v0, v2 - v0));
}
/* Point and normal on triangle. */
ccl_device_inline void triangle_point_normal(KernelGlobals kg,
const int object,
const int prim,
const float u,
const float v,
ccl_private float3 *P,
ccl_private float3 *Ng,
ccl_private int *shader)
{
/* load triangle vertices */
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 v0 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 v1 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 v2 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
/* compute point */
const float w = 1.0f - u - v;
*P = (w * v0 + u * v1 + v * v2);
/* get object flags */
const uint object_flag = kernel_data_fetch(object_flag, object);
/* compute normal */
if (object_negative_scale_applied(object_flag)) {
*Ng = normalize(cross(v2 - v0, v1 - v0));
}
else {
*Ng = normalize(cross(v1 - v0, v2 - v0));
}
/* shader */
*shader = kernel_data_fetch(tri_shader, prim);
}
/* Triangle vertex locations */
ccl_device_inline void triangle_vertices(KernelGlobals kg,
const int object,
const int prim,
float3 P[3])
{
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
P[0] = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
P[1] = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
P[2] = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
}
/* Triangle vertex locations and vertex normals */
ccl_device_inline void triangle_vertices_and_normals(KernelGlobals kg,
ccl_private const ShaderData *sd,
float3 P[3],
float3 N[3])
{
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
P[0] = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
P[1] = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
P[2] = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
const int normal_offset = kernel_data_fetch(objects, sd->object).normal_offset;
int i0, i1, i2;
if (sd->object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
i0 = sd->prim * 3 + 0;
i1 = sd->prim * 3 + 1;
i2 = sd->prim * 3 + 2;
}
else {
i0 = tri_vindex.x;
i1 = tri_vindex.y;
i2 = tri_vindex.z;
}
attribute_data_fetch_normals(kg, normal_offset, i0, i1, i2, N);
}
/* Interpolate smooth vertex normal from vertices */
ccl_device_inline float3 triangle_smooth_normal(
KernelGlobals kg, float3 Ng, int object, int object_flag, int prim, float u, float v)
{
const int normal_offset = kernel_data_fetch(objects, object).normal_offset;
int i0, i1, i2;
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
i0 = prim * 3 + 0;
i1 = prim * 3 + 1;
i2 = prim * 3 + 2;
}
else {
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
i0 = tri_vindex.x;
i1 = tri_vindex.y;
i2 = tri_vindex.z;
}
const float3 N = safe_normalize(
attribute_data_interpolate_normals(kg, normal_offset, i0, i1, i2, u, v));
return is_zero(N) ? Ng : N;
}
/* Compute triangle normals at the hit position, and offsetted positions in x and y direction for
* bump mapping. */
ccl_device_inline float3 triangle_smooth_normal(KernelGlobals kg,
const float3 Ng,
const int object,
const int object_flag,
const int prim,
const float u,
float v,
const differential du,
const differential dv,
ccl_private float3 &N_x,
ccl_private float3 &N_y)
{
const int normal_offset = kernel_data_fetch(objects, object).normal_offset;
int i0, i1, i2;
if (object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
i0 = prim * 3 + 0;
i1 = prim * 3 + 1;
i2 = prim * 3 + 2;
}
else {
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
i0 = tri_vindex.x;
i1 = tri_vindex.y;
i2 = tri_vindex.z;
}
float3 n[3];
attribute_data_fetch_normals(kg, normal_offset, i0, i1, i2, n);
const float3 N = safe_normalize(triangle_interpolate(u, v, n[0], n[1], n[2]));
N_x = safe_normalize(triangle_interpolate(u + du.dx, v + dv.dx, n[0], n[1], n[2]));
N_y = safe_normalize(triangle_interpolate(u + du.dy, v + dv.dy, n[0], n[1], n[2]));
N_x = is_zero(N_x) ? Ng : N_x;
N_y = is_zero(N_y) ? Ng : N_y;
return is_zero(N) ? Ng : N;
}
/* Special variation for normal mapping, where we want to match the unnormalized object
* space interpolation as assumed by normal map baking exactly. An exact match avoids
* discontinuities across UV seams.*/
ccl_device_inline float3 triangle_smooth_normal_unnormalized_object_space(
KernelGlobals kg, ccl_private const ShaderData *sd)
{
const int normal_offset = kernel_data_fetch(objects, sd->object).normal_offset;
int i0, i1, i2;
if (sd->object_flag & SD_OBJECT_HAS_CORNER_NORMALS) {
i0 = sd->prim * 3 + 0;
i1 = sd->prim * 3 + 1;
i2 = sd->prim * 3 + 2;
}
else {
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
i0 = tri_vindex.x;
i1 = tri_vindex.y;
i2 = tri_vindex.z;
}
float3 n[3];
attribute_data_fetch_normals(kg, normal_offset, i0, i1, i2, n);
if (sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED) {
object_inverse_normal_transform(kg, sd, &n[0]);
object_inverse_normal_transform(kg, sd, &n[1]);
object_inverse_normal_transform(kg, sd, &n[2]);
}
const float3 N = safe_normalize(triangle_interpolate(sd->u, sd->v, n[0], n[1], n[2]));
return is_zero(N) ? sd->Ng : N;
}
/* Ray differentials on triangle */
ccl_device_inline void triangle_dPdudv(KernelGlobals kg,
const int object,
const int prim,
ccl_private float3 *dPdu,
ccl_private float3 *dPdv)
{
/* fetch triangle vertex coordinates */
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 p0 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 p1 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 p2 = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
/* compute derivatives of P w.r.t. uv */
*dPdu = (p1 - p0);
*dPdv = (p2 - p0);
}
/* Partial derivative of f w.r.t. x, namely ∂f/∂x.
* f is a function of barycentric coordinates u, v, given by
* f(u, v) = f1 * u + f2 * v + f0 * (1 - u - v),
* the derivatives are
* ∂f/∂u = (f1 - f0), ∂f/∂v = (f2 - f0).
* The partial derivative in x is
* ∂f/∂x = ∂f/∂u * ∂u/∂x + ∂f/∂v * ∂v/∂x
* = (f1 - f0) * du.dx + (f2 - f0) * dv.dx. */
template<typename T>
ccl_device_inline T triangle_attribute_dfdx(const ccl_private differential &du,
const ccl_private differential &dv,
const ccl_private T &f0,
const ccl_private T &f1,
const ccl_private T &f2)
{
return du.dx * f1 + dv.dx * f2 - (du.dx + dv.dx) * f0;
}
/* Partial derivative of f w.r.t. in x, namely ∂f/∂y, similarly computed as ∂f/∂x above. */
template<typename T>
ccl_device_inline T triangle_attribute_dfdy(const ccl_private differential &du,
const ccl_private differential &dv,
const ccl_private T &f0,
const ccl_private T &f1,
const ccl_private T &f2)
{
return du.dy * f1 + dv.dy * f2 - (du.dy + dv.dy) * f0;
}
/* Read attributes on various triangle elements. T is the return type, which can be a plain type
* (float, float3, etc.) or a dual type (dual1, dual3, etc.) to include derivatives. */
template<typename T>
ccl_device T triangle_attribute(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc)
{
using BaseT = dual_base_t<T>;
if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_CORNER)) {
int i0, i1, i2;
if (desc.element & ATTR_ELEMENT_VERTEX) {
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, sd->prim);
i0 = tri_vindex.x;
i1 = tri_vindex.y;
i2 = tri_vindex.z;
}
else {
/* Corner attributes. */
const int tri = sd->prim * 3;
i0 = tri + 0;
i1 = tri + 1;
i2 = tri + 2;
}
BaseT f[3];
attribute_data_fetch_3<BaseT>(kg, desc.element, desc.offset, i0, i1, i2, f);
if constexpr (is_dual_v<T>) {
T result;
result.val = triangle_interpolate(sd->u, sd->v, f[0], f[1], f[2]);
#ifdef __RAY_DIFFERENTIALS__
result.dx = triangle_attribute_dfdx(sd->du, sd->dv, f[0], f[1], f[2]);
result.dy = triangle_attribute_dfdy(sd->du, sd->dv, f[0], f[1], f[2]);
#endif
return result;
}
else {
return triangle_interpolate(sd->u, sd->v, f[0], f[1], f[2]);
}
}
if (desc.element & ATTR_ELEMENT_FACE) {
return T(attribute_data_fetch<BaseT>(kg, desc.element, desc.offset + sd->prim));
}
return make_zero<T>();
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,190 @@
/* SPDX-FileCopyrightText: 2014-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Triangle/Ray intersections.
*
* For BVH ray intersection we use a precomputed triangle storage to accelerate
* intersection at the cost of more memory usage.
*/
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/geom_intersect.h"
#include "kernel/geom/object.h"
#include "kernel/geom/triangle.h"
#include "util/math_float3.h"
#include "util/math_intersect.h"
CCL_NAMESPACE_BEGIN
ccl_device_inline bool triangle_intersect(KernelGlobals kg,
ccl_private Intersection *isect,
const float3 P,
const float3 dir,
const float tmin,
const float tmax,
const uint visibility,
const int object,
const int prim,
const int prim_addr)
{
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 tri_a = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 tri_b = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 tri_c = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
float t;
float u;
float v;
if (ray_triangle_intersect(P, dir, tmin, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) {
#ifdef __VISIBILITY_FLAG__
/* Visibility flag test. we do it here under the assumption
* that most triangles are culled by node flags.
*/
if (kernel_data_fetch(prim_visibility, prim_addr) & visibility)
#endif
{
isect->object = object;
isect->prim = prim;
isect->type = PRIMITIVE_TRIANGLE;
isect->u = u;
isect->v = v;
isect->t = t;
return true;
}
}
return false;
}
/* Special ray intersection routines for subsurface scattering. In that case we
* only want to intersect with primitives in the same object, and if case of
* multiple hits we pick a single random primitive as the intersection point.
* Returns whether traversal should be stopped.
*/
#ifdef __BVH_LOCAL__
ccl_device_inline bool triangle_intersect_local(KernelGlobals kg,
ccl_private LocalIntersection *local_isect,
const float3 P,
const float3 dir,
const int object,
const int prim,
const float tmin,
const float tmax,
ccl_private uint *lcg_state,
const int max_hits)
{
const int position_offset = kernel_data_fetch(objects, object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, prim);
const float3 tri_a = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 tri_b = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 tri_c = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
float t;
float u;
float v;
if (!ray_triangle_intersect(P, dir, tmin, tmax, tri_a, tri_b, tri_c, &u, &v, &t)) {
return false;
}
/* If no actual hit information is requested, just return here. */
if (max_hits == 0) {
return true;
}
const int hit_index = local_intersect_get_record_index(local_isect, t, lcg_state, max_hits);
if (hit_index == -1) {
return false;
}
/* Record intersection. */
ccl_private Intersection *isect = &local_isect->hits[hit_index];
isect->prim = prim;
isect->object = object;
isect->type = PRIMITIVE_TRIANGLE;
isect->u = u;
isect->v = v;
isect->t = t;
/* Record geometric normal. */
local_isect->Ng[hit_index] = normalize(cross(tri_b - tri_a, tri_c - tri_a));
return false;
}
#endif /* __BVH_LOCAL__ */
/**
* Use the barycentric coordinates to get the intersection location
*/
ccl_device_inline float3 triangle_point_from_uv(KernelGlobals kg,
ccl_private ShaderData *sd,
const int isect_prim,
const float u,
const float v)
{
const int position_offset = kernel_data_fetch(objects, sd->object).position_offset;
const uint3 tri_vindex = kernel_data_fetch(tri_vindex, isect_prim);
const float3 tri_a = kernel_data_fetch(tri_verts, position_offset + tri_vindex.x);
const float3 tri_b = kernel_data_fetch(tri_verts, position_offset + tri_vindex.y);
const float3 tri_c = kernel_data_fetch(tri_verts, position_offset + tri_vindex.z);
/* This appears to give slightly better precision than interpolating with w = (1 - u - v). */
float3 P = tri_a + u * (tri_b - tri_a) + v * (tri_c - tri_a);
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
const Transform tfm = object_get_transform(kg, sd);
P = transform_point(&tfm, P);
}
return P;
}
/**
* Use the barycentric coordinates to get the intersection location,
* but with vertex coordinates specified.
*/
ccl_device_inline float3 triangle_point_from_uv_and_verts(KernelGlobals kg,
ccl_private ShaderData *sd,
const float u,
const float v,
const float3 verts[3])
{
/* This appears to give slightly better precision than interpolating with w = (1 - u - v). */
float3 P = verts[0] + u * (verts[1] - verts[0]) + v * (verts[2] - verts[0]);
if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) {
const Transform tfm = object_get_transform(kg, sd);
P = transform_point(&tfm, P);
}
return P;
}
ccl_device_inline void triangle_shader_setup(KernelGlobals kg, ccl_private ShaderData *sd)
{
sd->shader = kernel_data_fetch(tri_shader, sd->prim);
sd->P = triangle_point_from_uv(kg, sd, sd->prim, sd->u, sd->v);
/* Normals. */
const float3 Ng = triangle_normal(kg, sd);
sd->Ng = Ng;
sd->N = Ng;
/* Smooth normal. */
if (sd->shader & SHADER_SMOOTH_NORMAL) {
sd->N = triangle_smooth_normal(kg, Ng, sd->object, sd->object_flag, sd->prim, sd->u, sd->v);
}
#ifdef __DPDU__
/* dPdu/dPdv */
triangle_dPdudv(kg, sd->object, sd->prim, &sd->dPdu, &sd->dPdv);
#endif
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,119 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
/* Volume Primitive
*
* Volumes are just regions inside meshes with the mesh surface as boundaries.
* There isn't as much data to access as for surfaces, there is only a position
* to do lookups in 3D voxel or procedural textures.
*
* 3D voxel textures can be assigned as attributes per mesh, which means the
* same shader can be used for volume objects with different densities, etc. */
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/object.h"
#include "kernel/util/image_3d.h"
CCL_NAMESPACE_BEGIN
#ifdef __VOLUME__
/* Return position normalized to 0..1 in mesh bounds */
template<typename Float3Type>
ccl_device_inline Float3Type volume_normalized_position(KernelGlobals kg,
const ccl_private ShaderData *sd,
Float3Type P)
{
const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_GENERATED_TRANSFORM);
object_inverse_position_transform_if_object(kg, sd, &P);
if (is_attribute_found(desc)) {
const Transform tfm = primitive_attribute_matrix(kg, desc);
P = transform_point(&tfm, P);
}
return P;
}
template<typename T> ccl_device_inline T volume_attribute_value(const float4 value);
ccl_device_template_spec float volume_attribute_value(const float4 value)
{
return average(make_float3(value));
}
ccl_device_template_spec float2 volume_attribute_value(const float4 value)
{
return make_float2(value.x, value.y);
}
ccl_device_template_spec float3 volume_attribute_value(const float4 value)
{
return make_float3(value);
}
ccl_device_template_spec float4 volume_attribute_value(const float4 value)
{
return value;
}
ccl_device float volume_attribute_alpha(const float4 value)
{
return value.w;
}
ccl_device float4 volume_attribute_float4(KernelGlobals kg,
ccl_private ShaderData *sd,
const AttributeDescriptor desc,
const bool stochastic)
{
if (desc.element & (ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
switch (desc.type) {
case NODE_ATTR_FLOAT: {
const float f = kernel_data_fetch(attributes_float, desc.offset);
return make_float4(f, f, f, 1.0f);
}
case NODE_ATTR_FLOAT2: {
const float2 f = kernel_data_fetch(attributes_float2, desc.offset);
return make_float4(f.x, f.y, 0.0f, 1.0f);
}
case NODE_ATTR_FLOAT3: {
const float3 f = kernel_data_fetch(attributes_float3, desc.offset);
return make_float4(f.x, f.y, f.z, 1.0f);
}
case NODE_ATTR_FLOAT4:
case NODE_ATTR_RGBA:
return kernel_data_fetch(attributes_float4, desc.offset);
case NODE_ATTR_MATRIX:
return zero_float4();
}
}
if (desc.element & ATTR_ELEMENT_VOXEL) {
/* todo: optimize this so we don't have to transform both here and in
* kernel_image_interp_3d when possible. Also could optimize for the
* common case where transform is translation/scale only. */
float3 P = sd->P;
object_inverse_position_transform(kg, sd, &P);
const InterpolationType interp = (sd->flag & SD_VOLUME_CUBIC) ? INTERPOLATION_CUBIC :
INTERPOLATION_NONE;
const float4 value = kernel_image_interp_3d(kg, sd, desc.offset, P, interp, stochastic);
if (value.w > 1e-6f && value.w != 1.0f) {
/* For RGBA colors, unpremultiply after interpolation. */
return make_float4(make_float3(value) / value.w, value.w);
}
return value;
}
return zero_float4();
}
#endif
CCL_NAMESPACE_END