Files
test2/intern/cycles/kernel/geom/curve.h
Weizhen Huang f4e4f19af2 Cleanup: Cycles: improve comments and variable names of bump-related functions
- Rename dx/dy -> dfdx/dfdy to match the actual computed quantity
- Add template functions to compute dfdx/dfdy on triangles for sharing
  among different data types
- Add documentation to some functions
- Some code shuffling that makes it easier to scale dfdx/dfdy in the
  future
- Some other trivial changes
2025-02-05 13:39:27 +01:00

362 lines
11 KiB
C++

/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "kernel/globals.h"
#include "kernel/geom/attribute.h"
#include "kernel/geom/motion_curve.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, and compute the partial derivatives if requested. */
ccl_device float curve_attribute_float(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float *dfdx,
ccl_private float *dfdy)
{
if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) {
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 float f0 = kernel_data_fetch(attributes_float, desc.offset + k0);
const float f1 = kernel_data_fetch(attributes_float, desc.offset + k1);
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = curve_attribute_dfdx(sd->du, f0, f1);
}
if (dfdy) {
*dfdy = curve_attribute_dfdy(sd->du, f0, f1);
}
# endif
return (1.0f - sd->u) * f0 + sd->u * f1;
}
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = 0.0f;
}
if (dfdy) {
*dfdy = 0.0f;
}
# endif
if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
const int offset = (desc.element == ATTR_ELEMENT_CURVE) ? desc.offset + sd->prim : desc.offset;
return kernel_data_fetch(attributes_float, offset);
}
return 0.0f;
}
ccl_device float2 curve_attribute_float2(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float2 *dfdx,
ccl_private float2 *dfdy)
{
if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) {
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 float2 f0 = kernel_data_fetch(attributes_float2, desc.offset + k0);
const float2 f1 = kernel_data_fetch(attributes_float2, desc.offset + k1);
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = curve_attribute_dfdx(sd->du, f0, f1);
}
if (dfdy) {
*dfdy = curve_attribute_dfdy(sd->du, f0, f1);
}
# endif
return (1.0f - sd->u) * f0 + sd->u * f1;
}
/* 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? */
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = zero_float2();
}
if (dfdy) {
*dfdy = zero_float2();
}
# endif
if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
const int offset = (desc.element == ATTR_ELEMENT_CURVE) ? desc.offset + sd->prim : desc.offset;
return kernel_data_fetch(attributes_float2, offset);
}
return zero_float2();
}
ccl_device float3 curve_attribute_float3(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float3 *dfdx,
ccl_private float3 *dfdy)
{
if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) {
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 float3 f0 = kernel_data_fetch(attributes_float3, desc.offset + k0);
const float3 f1 = kernel_data_fetch(attributes_float3, desc.offset + k1);
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = curve_attribute_dfdx(sd->du, f0, f1);
}
if (dfdy) {
*dfdy = curve_attribute_dfdy(sd->du, f0, f1);
}
# endif
return (1.0f - sd->u) * f0 + sd->u * f1;
}
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = zero_float3();
}
if (dfdy) {
*dfdy = zero_float3();
}
# endif
if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
const int offset = (desc.element == ATTR_ELEMENT_CURVE) ? desc.offset + sd->prim : desc.offset;
return kernel_data_fetch(attributes_float3, offset);
}
return zero_float3();
}
ccl_device float4 curve_attribute_float4(KernelGlobals kg,
const ccl_private ShaderData *sd,
const AttributeDescriptor desc,
ccl_private float4 *dfdx,
ccl_private float4 *dfdy)
{
if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) {
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(attributes_float4, desc.offset + k0);
const float4 f1 = kernel_data_fetch(attributes_float4, desc.offset + k1);
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = curve_attribute_dfdx(sd->du, f0, f1);
}
if (dfdy) {
*dfdy = curve_attribute_dfdy(sd->du, f0, f1);
}
# endif
return (1.0f - sd->u) * f0 + sd->u * f1;
}
# ifdef __RAY_DIFFERENTIALS__
if (dfdx) {
*dfdx = zero_float4();
}
if (dfdy) {
*dfdy = zero_float4();
}
# endif
if (desc.element & (ATTR_ELEMENT_CURVE | ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) {
const int offset = (desc.element == ATTR_ELEMENT_CURVE) ? desc.offset + sd->prim : desc.offset;
return kernel_data_fetch(attributes_float4, offset);
}
return zero_float4();
}
/* Curve thickness */
ccl_device float curve_thickness(KernelGlobals kg, const ccl_private ShaderData *sd)
{
float r = 0.0f;
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;
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
{
P_curve[0] = kernel_data_fetch(curve_keys, k0);
P_curve[1] = kernel_data_fetch(curve_keys, k1);
}
r = (P_curve[1].w - P_curve[0].w) * sd->u + P_curve[0].w;
}
return r * 2.0f;
}
/* 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 (desc.offset != ATTR_STD_NOT_FOUND) ?
curve_attribute_float(kg, sd, desc, nullptr, nullptr) :
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];
P_curve[0] = kernel_data_fetch(curve_keys, k0);
P_curve[1] = kernel_data_fetch(curve_keys, 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(KernelGlobals kg, 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