Add Chromium-only Blender WebEngine parity work
This commit is contained in:
264
blender-5.2.0/intern/cycles/kernel/geom/attribute.h
Normal file
264
blender-5.2.0/intern/cycles/kernel/geom/attribute.h
Normal 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
|
||||
239
blender-5.2.0/intern/cycles/kernel/geom/curve.h
Normal file
239
blender-5.2.0/intern/cycles/kernel/geom/curve.h
Normal 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
|
||||
958
blender-5.2.0/intern/cycles/kernel/geom/curve_intersect.h
Normal file
958
blender-5.2.0/intern/cycles/kernel/geom/curve_intersect.h
Normal 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
|
||||
64
blender-5.2.0/intern/cycles/kernel/geom/geom_intersect.h
Normal file
64
blender-5.2.0/intern/cycles/kernel/geom/geom_intersect.h
Normal 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
|
||||
142
blender-5.2.0/intern/cycles/kernel/geom/motion_curve.h
Normal file
142
blender-5.2.0/intern/cycles/kernel/geom/motion_curve.h
Normal 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
|
||||
68
blender-5.2.0/intern/cycles/kernel/geom/motion_point.h
Normal file
68
blender-5.2.0/intern/cycles/kernel/geom/motion_point.h
Normal 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
|
||||
260
blender-5.2.0/intern/cycles/kernel/geom/motion_triangle.h
Normal file
260
blender-5.2.0/intern/cycles/kernel/geom/motion_triangle.h
Normal 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
512
blender-5.2.0/intern/cycles/kernel/geom/object.h
Normal file
512
blender-5.2.0/intern/cycles/kernel/geom/object.h
Normal 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
|
||||
100
blender-5.2.0/intern/cycles/kernel/geom/point.h
Normal file
100
blender-5.2.0/intern/cycles/kernel/geom/point.h
Normal 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
|
||||
130
blender-5.2.0/intern/cycles/kernel/geom/point_intersect.h
Normal file
130
blender-5.2.0/intern/cycles/kernel/geom/point_intersect.h
Normal 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, ¢er);
|
||||
}
|
||||
|
||||
/* 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
|
||||
373
blender-5.2.0/intern/cycles/kernel/geom/primitive.h
Normal file
373
blender-5.2.0/intern/cycles/kernel/geom/primitive.h
Normal 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
|
||||
476
blender-5.2.0/intern/cycles/kernel/geom/shader_data.h
Normal file
476
blender-5.2.0/intern/cycles/kernel/geom/shader_data.h
Normal 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
|
||||
340
blender-5.2.0/intern/cycles/kernel/geom/triangle.h
Normal file
340
blender-5.2.0/intern/cycles/kernel/geom/triangle.h
Normal 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
|
||||
190
blender-5.2.0/intern/cycles/kernel/geom/triangle_intersect.h
Normal file
190
blender-5.2.0/intern/cycles/kernel/geom/triangle_intersect.h
Normal 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
|
||||
119
blender-5.2.0/intern/cycles/kernel/geom/volume.h
Normal file
119
blender-5.2.0/intern/cycles/kernel/geom/volume.h
Normal 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
|
||||
Reference in New Issue
Block a user