/* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ #pragma once 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__ /* Reading attributes on various curve elements */ ccl_device float curve_attribute_float(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float *dx, ccl_private float *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float f0 = kernel_tex_fetch(__attributes_float, desc.offset + k0); float f1 = kernel_tex_fetch(__attributes_float, desc.offset + k1); # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = sd->du.dx * (f1 - f0); if (dy) *dy = 0.0f; # endif return (1.0f - sd->u) * f0 + sd->u * f1; } else { # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = 0.0f; if (dy) *dy = 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_tex_fetch(__attributes_float, offset); } else { return 0.0f; } } } ccl_device float2 curve_attribute_float2(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float2 *dx, ccl_private float2 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float2 f0 = kernel_tex_fetch(__attributes_float2, desc.offset + k0); float2 f1 = kernel_tex_fetch(__attributes_float2, desc.offset + k1); # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = sd->du.dx * (f1 - f0); if (dy) *dy = make_float2(0.0f, 0.0f); # endif return (1.0f - sd->u) * f0 + sd->u * f1; } else { /* 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 (dx) *dx = make_float2(0.0f, 0.0f); if (dy) *dy = make_float2(0.0f, 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_tex_fetch(__attributes_float2, offset); } else { return make_float2(0.0f, 0.0f); } } } ccl_device float3 curve_attribute_float3(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float3 *dx, ccl_private float3 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float3 f0 = kernel_tex_fetch(__attributes_float3, desc.offset + k0); float3 f1 = kernel_tex_fetch(__attributes_float3, desc.offset + k1); # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = sd->du.dx * (f1 - f0); if (dy) *dy = make_float3(0.0f, 0.0f, 0.0f); # endif return (1.0f - sd->u) * f0 + sd->u * f1; } else { # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = make_float3(0.0f, 0.0f, 0.0f); if (dy) *dy = make_float3(0.0f, 0.0f, 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_tex_fetch(__attributes_float3, offset); } else { return make_float3(0.0f, 0.0f, 0.0f); } } } ccl_device float4 curve_attribute_float4(KernelGlobals kg, ccl_private const ShaderData *sd, const AttributeDescriptor desc, ccl_private float4 *dx, ccl_private float4 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 f0 = kernel_tex_fetch(__attributes_float4, desc.offset + k0); float4 f1 = kernel_tex_fetch(__attributes_float4, desc.offset + k1); # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = sd->du.dx * (f1 - f0); if (dy) *dy = zero_float4(); # endif return (1.0f - sd->u) * f0 + sd->u * f1; } else { # ifdef __RAY_DIFFERENTIALS__ if (dx) *dx = zero_float4(); if (dy) *dy = 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_tex_fetch(__attributes_float4, offset); } else { return zero_float4(); } } } /* Curve thickness */ ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData *sd) { float r = 0.0f; if (sd->type & PRIMITIVE_CURVE) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 P_curve[2]; if (!(sd->type & PRIMITIVE_MOTION)) { P_curve[0] = kernel_tex_fetch(__curve_keys, k0); P_curve[1] = kernel_tex_fetch(__curve_keys, k1); } else { motion_curve_keys_linear(kg, sd->object, sd->prim, sd->time, k0, k1, P_curve); } 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, ccl_private const 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, NULL, NULL) : 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, ccl_private const ShaderData *sd) { KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 P_curve[2]; P_curve[0] = kernel_tex_fetch(__curve_keys, k0); P_curve[1] = kernel_tex_fetch(__curve_keys, k1); return float4_to_float3(P_curve[1]) * sd->u + float4_to_float3(P_curve[0]) * (1.0f - sd->u); } /* Curve tangent normal */ ccl_device float3 curve_tangent_normal(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 tgN = make_float3(0.0f, 0.0f, 0.0f); if (sd->type & PRIMITIVE_CURVE) { tgN = -(-sd->I - sd->dPdu * (dot(sd->dPdu, -sd->I) / 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) { 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