CultMath: Cycles node semantics (Math, Map Range, Clamp, Mix, RGB to BW, ramp index) as math.Cycles.cs and CultMath.Cycles.hlsl under Apache-2.0
Ports of Blender v5.2.2's intern/cycles kernel functions, one function per node
operation, with the HLSL mirror, the Unity copy, the lowering table entry for a
separate Apache-2.0 GLSL file, the golden-fixture families, the package licence
expressions and the notices section.
Builds. Does not pass yet: CultMath.Cycles.glsl and the regenerated golden
fixture arrive in a later commit, so CommittedGlslEqualsLowering and
GoldenFixtureMatchesCSharp fail at this commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 03:59:59 +02:00
// SPDX-License-Identifier: Apache-2.0
//
// Cycles node semantics: what Blender's Cycles renderer computes for the Math, Map Range, Clamp, Mix and
// RGB to BW nodes and for a Color Ramp lookup, ported function by function from the Cycles kernel
// (intern/cycles of the Blender v5.2.2 tag; SPDX-FileCopyrightText: 2011-2022 Blender Foundation, Apache-2.0):
// kernel/svm/{math_util,map_range,clamp,mix,color_util,ramp,convert}.h, util/math_base.h, util/color.h and
// util/math_float3.h. Each function's comment names its Cycles source. Modified by GameCult: Cycles C++
// becomes C#, a function per node operation, and the node's socket defaults and clamp options are left to
// the caller. This file is Apache-2.0 (section 4: it was changed); the rest of CultMath is MIT, except the
// MPL-2.0 Phacelle files (see THIRD-PARTY-NOTICES.md). shaders/CultMath.Cycles.hlsl mirrors it bit for bit.
//
// Operands are finite floats. NaN and infinite operands are outside the oracle's reach and the mirror's.
// CyclesOracleTests holds every function to the results of Blender 5.2.2's own CPU Cycles.
namespace CultMath ;
public static partial class math
{
// FLT_EPSILON, the least epsilon a Compare node honours.
private const float CYCLES_FLT_EPSILON = 1.1920929e-07f ;
/// <summary>The scene-linear red luminance coefficient Cycles' RGB to BW node reads from Blender 5.2.2's default OCIO config.</summary>
public const float CYCLES_RGB_TO_Y_R = 0.2126729f ;
/// <summary>The scene-linear green luminance coefficient Cycles' RGB to BW node reads from Blender 5.2.2's default OCIO config.</summary>
public const float CYCLES_RGB_TO_Y_G = 0.7151522f ;
/// <summary>The scene-linear blue luminance coefficient Cycles' RGB to BW node reads from Blender 5.2.2's default OCIO config.</summary>
public const float CYCLES_RGB_TO_Y_B = 0.0721750f ;
// ---- Math node (kernel/svm/math_util.h svm_math; util/math_base.h) ----
/// <summary>Math node Divide (safe_divide): a / b, or 0 when b is 0.</summary>
public static float cycles_divide ( float a , float b ) = > b ! = 0.0f ? a / b : 0.0f ;
/// <summary>
/// Math node Power (safe_powf with compatible_powf): 0 for a negative base with a fractional exponent,
/// 1 for exponent 0, 0 for base 0. A negative base takes an integral exponent's parity. An exponent past
/// the int range counts as fractional for a negative base, where Cycles' int round trip cannot hold it.
/// </summary>
public static float cycles_power ( float a , float b )
{
var whole = b > = 0.0f ? floor ( b ) : ceil ( b ) ;
if ( a < 0.0f & & ( b ! = whole | | b > = 2147483648.0f | | b < - 2147483648.0f ) )
return 0.0f ;
if ( b = = 0.0f )
return 1.0f ;
if ( a = = 0.0f )
return 0.0f ;
if ( a < 0.0f )
{
var magnitude = pow ( - a , b ) ;
return b - 2.0f * floor ( 0.5f * b ) ! = 0.0f ? - magnitude : magnitude ;
}
return pow ( a , b ) ;
}
/// <summary>Math node Logarithm (safe_logf): log(a) / log(b), or 0 when a or b is not positive or log(b) is 0.</summary>
public static float cycles_logarithm ( float a , float b )
{
if ( a < = 0.0f | | b < = 0.0f )
return 0.0f ;
var denominator = log ( b ) ;
return denominator ! = 0.0f ? log ( a ) / denominator : 0.0f ;
}
/// <summary>Math node Square Root (safe_sqrtf): the square root of max(a, 0).</summary>
public static float cycles_sqrt ( float a ) = > sqrt ( max ( a , 0.0f ) ) ;
/// <summary>Math node Inverse Square Root (inversesqrtf): 1 / sqrt(a), or 0 when a is not positive.</summary>
public static float cycles_inverse_sqrt ( float a ) = > a > 0.0f ? 1.0f / sqrt ( a ) : 0.0f ;
/// <summary>
/// Math node Truncated Modulo (safe_modulo), the remainder with the dividend's sign, or 0 when b is 0.
/// Cycles computes fmodf. GLSL has no fmod and HLSL's is this quotient form on a GPU, so this is the form
/// every CultMath target shares: it is not fmodf bit for bit, and CyclesOracleTests states how far it is.
/// </summary>
public static float cycles_modulo ( float a , float b )
{
var quotient = a / b ;
return b ! = 0.0f ? a - b * ( quotient > = 0.0f ? floor ( quotient ) : ceil ( quotient ) ) : 0.0f ;
}
/// <summary>Math node Floored Modulo (safe_floored_modulo): a - floor(a / b) b, or 0 when b is 0.</summary>
public static float cycles_floored_modulo ( float a , float b ) = > b ! = 0.0f ? a - floor ( a / b ) * b : 0.0f ;
/// <summary>Math node Wrap (wrapf; note the argument order): value wrapped into [min_value, max_value), or min_value for an empty range.</summary>
public static float cycles_wrap ( float value , float max_value , float min_value )
{
var range = max_value - min_value ;
return range ! = 0.0f ? value - ( range * floor ( ( value - min_value ) / range ) ) : min_value ;
}
/// <summary>Math node Snap: floor(safe_divide(a, b)) b.</summary>
public static float cycles_snap ( float a , float b ) = > floor ( cycles_divide ( a , b ) ) * b ;
/// <summary>Math node Ping-Pong (pingpongf): a triangle wave of a with period 2 b, or 0 when b is 0.</summary>
public static float cycles_pingpong ( float a , float b )
{
var scaled = ( a - b ) / ( b * 2.0f ) ;
return b ! = 0.0f ? abs ( ( scaled - floor ( scaled ) ) * b * 2.0f - b ) : 0.0f ;
}
/// <summary>Math node Smooth Minimum (smoothminf): the polynomial smooth minimum of width k, or min(a, b) when k is 0.</summary>
public static float cycles_smooth_min ( float a , float b , float k )
{
var h = max ( k - abs ( a - b ) , 0.0f ) / k ;
return k ! = 0.0f ? min ( a , b ) - h * h * h * k * 0.16666667f : min ( a , b ) ;
}
/// <summary>Math node Smooth Maximum: -smoothminf(-a, -b, k).</summary>
public static float cycles_smooth_max ( float a , float b , float k ) = > - cycles_smooth_min ( - a , - b , k ) ;
/// <summary>Math node Compare: 1 when a equals b or they differ by at most max(epsilon, FLT_EPSILON), else 0.</summary>
public static float cycles_compare ( float a , float b , float epsilon ) = >
( a = = b | | abs ( a - b ) < = max ( epsilon , CYCLES_FLT_EPSILON ) ) ? 1.0f : 0.0f ;
/// <summary>Math node Round: floor(a + 0.5).</summary>
public static float cycles_round ( float a ) = > floor ( a + 0.5f ) ;
/// <summary>Math node Truncate: floor for a >= 0, ceil otherwise.</summary>
public static float cycles_trunc ( float a ) = > a > = 0.0f ? floor ( a ) : ceil ( a ) ;
/// <summary>Math node Fraction: a - floor(a).</summary>
public static float cycles_fract ( float a ) = > a - floor ( a ) ;
/// <summary>Math node Sign (compatible_signf): 0 for 0, -1 below, 1 above.</summary>
public static float cycles_sign ( float a ) = > a = = 0.0f ? 0.0f : ( a < 0.0f ? - 1.0f : 1.0f ) ;
// ---- Map Range node (kernel/svm/map_range.h svm_node_map_range) ----
// A Map Range node's Clamp option is a Clamp node of type Range on its result (MapRangeNode::expand):
// compose cycles_clamp_range over these.
/// <summary>Map Range, Linear: to_min + (value - from_min) / (from_max - from_min) (to_max - to_min), or 0 when from_max equals from_min.</summary>
public static float cycles_map_range_linear ( float value , float from_min , float from_max , float to_min , float to_max )
{
var factor = ( value - from_min ) / ( from_max - from_min ) ;
return from_max ! = from_min ? to_min + factor * ( to_max - to_min ) : 0.0f ;
}
/// <summary>Map Range, Stepped: the linear factor quantized to `steps` steps (0 when steps is not positive), or 0 when from_max equals from_min.</summary>
public static float cycles_map_range_stepped ( float value , float from_min , float from_max , float to_min , float to_max , float steps )
{
var factor = ( value - from_min ) / ( from_max - from_min ) ;
factor = steps > 0.0f ? floor ( factor * ( steps + 1.0f ) ) / steps : 0.0f ;
return from_max ! = from_min ? to_min + factor * ( to_max - to_min ) : 0.0f ;
}
/// <summary>
/// Map Range, Smooth Step: the value goes through smoothstep(from_min, from_max, .), flipped when from_min
/// is above from_max (util/math_base.h smoothstep), or 0 when from_max equals from_min.
/// </summary>
public static float cycles_map_range_smoothstep ( float value , float from_min , float from_max , float to_min , float to_max )
{
var low = min ( from_min , from_max ) ;
var high = max ( from_min , from_max ) ;
var t = ( value - low ) / ( high - low ) ;
var eased = value < low ? 0.0f : ( value > = high ? 1.0f : ( 3.0f - 2.0f * t ) * ( t * t ) ) ;
var factor = from_min > from_max ? 1.0f - eased : eased ;
return from_max ! = from_min ? to_min + factor * ( to_max - to_min ) : 0.0f ;
}
/// <summary>Map Range, Smoother Step (map_range.h smootherstep), flipped when from_min is above from_max, or 0 when from_max equals from_min.</summary>
public static float cycles_map_range_smootherstep ( float value , float from_min , float from_max , float to_min , float to_max )
{
var low = min ( from_min , from_max ) ;
var high = max ( from_min , from_max ) ;
var x = clamp ( cycles_divide ( value - low , high - low ) , 0.0f , 1.0f ) ;
var eased = x * x * x * ( x * ( x * 6.0f - 15.0f ) + 10.0f ) ;
var factor = from_min > from_max ? 1.0f - eased : eased ;
return from_max ! = from_min ? to_min + factor * ( to_max - to_min ) : 0.0f ;
}
// ---- Clamp node (kernel/svm/clamp.h svm_node_clamp) ----
/// <summary>Clamp node, Min Max: clamp(value, min_value, max_value).</summary>
public static float cycles_clamp_minmax ( float value , float min_value , float max_value ) = > clamp ( value , min_value , max_value ) ;
/// <summary>Clamp node, Range: as Min Max, with the bounds swapped when min_value is above max_value.</summary>
public static float cycles_clamp_range ( float value , float min_value , float max_value ) = >
min_value > max_value ? clamp ( value , max_value , min_value ) : clamp ( value , min_value , max_value ) ;
// ---- Mix node (kernel/svm/mix.h, color_util.h) ----
// Factor clamping (svm_node_mix_color's saturatef) and result clamping (saturate) are the caller's.
/// <summary>Mix node, Float (svm_node_mix_float): a (1 - t) + b t.</summary>
public static float cycles_mix_float ( float t , float a , float b ) = > a * ( 1.0f - t ) + b * t ;
/// <summary>Mix node, Vector, uniform factor (svm_node_mix_vector): a (1 - t) + b t per component.</summary>
public static float3 cycles_mix_vector ( float t , float3 a , float3 b ) = > a * ( new float3 ( 1.0f , 1.0f , 1.0f ) - t ) + b * t ;
/// <summary>Mix node, Mix blend mode (svm_mix_blend).</summary>
public static float3 cycles_mix_blend ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( col2 - col1 ) ;
/// <summary>Mix node, Add blend mode (svm_mix_add).</summary>
public static float3 cycles_mix_add ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( ( col1 + col2 ) - col1 ) ;
/// <summary>Mix node, Multiply blend mode (svm_mix_mul).</summary>
public static float3 cycles_mix_mul ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( col1 * col2 - col1 ) ;
/// <summary>Mix node, Screen blend mode (svm_mix_screen).</summary>
public static float3 cycles_mix_screen ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
var one = new float3 ( 1.0f , 1.0f , 1.0f ) ;
return one - ( new float3 ( tm , tm , tm ) + t * ( one - col2 ) ) * ( one - col1 ) ;
}
/// <summary>Mix node, Overlay blend mode (svm_mix_overlay).</summary>
public static float3 cycles_mix_overlay ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
return new float3 (
col1 . x < 0.5f ? col1 . x * ( tm + 2.0f * t * col2 . x ) : 1.0f - ( tm + 2.0f * t * ( 1.0f - col2 . x ) ) * ( 1.0f - col1 . x ) ,
col1 . y < 0.5f ? col1 . y * ( tm + 2.0f * t * col2 . y ) : 1.0f - ( tm + 2.0f * t * ( 1.0f - col2 . y ) ) * ( 1.0f - col1 . y ) ,
col1 . z < 0.5f ? col1 . z * ( tm + 2.0f * t * col2 . z ) : 1.0f - ( tm + 2.0f * t * ( 1.0f - col2 . z ) ) * ( 1.0f - col1 . z ) ) ;
}
/// <summary>Mix node, Subtract blend mode (svm_mix_sub).</summary>
public static float3 cycles_mix_sub ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( ( col1 - col2 ) - col1 ) ;
/// <summary>Mix node, Divide blend mode (svm_mix_div): a channel whose col2 is 0 keeps col1.</summary>
public static float3 cycles_mix_div ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
return new float3 (
col2 . x ! = 0.0f ? tm * col1 . x + t * col1 . x / col2 . x : col1 . x ,
col2 . y ! = 0.0f ? tm * col1 . y + t * col1 . y / col2 . y : col1 . y ,
col2 . z ! = 0.0f ? tm * col1 . z + t * col1 . z / col2 . z : col1 . z ) ;
}
/// <summary>Mix node, Difference blend mode (svm_mix_diff).</summary>
public static float3 cycles_mix_diff ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( abs ( col1 - col2 ) - col1 ) ;
/// <summary>Mix node, Exclusion blend mode (svm_mix_exclusion).</summary>
public static float3 cycles_mix_exclusion ( float t , float3 col1 , float3 col2 ) = >
max ( col1 + t * ( ( col1 + col2 - 2.0f * col1 * col2 ) - col1 ) , new float3 ( 0.0f , 0.0f , 0.0f ) ) ;
/// <summary>Mix node, Darken blend mode (svm_mix_dark).</summary>
public static float3 cycles_mix_dark ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( min ( col1 , col2 ) - col1 ) ;
/// <summary>Mix node, Lighten blend mode (svm_mix_light).</summary>
public static float3 cycles_mix_light ( float t , float3 col1 , float3 col2 ) = > col1 + t * ( max ( col1 , col2 ) - col1 ) ;
/// <summary>Mix node, Dodge blend mode (svm_mix_dodge).</summary>
public static float3 cycles_mix_dodge ( float t , float3 col1 , float3 col2 )
{
var divisor = new float3 ( 1.0f , 1.0f , 1.0f ) - t * col2 ;
return new float3 (
col1 . x ! = 0.0f ? ( divisor . x < = 0.0f ? 1.0f : min ( col1 . x / divisor . x , 1.0f ) ) : col1 . x ,
col1 . y ! = 0.0f ? ( divisor . y < = 0.0f ? 1.0f : min ( col1 . y / divisor . y , 1.0f ) ) : col1 . y ,
col1 . z ! = 0.0f ? ( divisor . z < = 0.0f ? 1.0f : min ( col1 . z / divisor . z , 1.0f ) ) : col1 . z ) ;
}
/// <summary>Mix node, Burn blend mode (svm_mix_burn).</summary>
public static float3 cycles_mix_burn ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
var divisor = tm + t * col2 ;
var burnt = new float3 ( 1.0f , 1.0f , 1.0f ) - ( new float3 ( 1.0f , 1.0f , 1.0f ) - col1 ) / divisor ;
return new float3 (
divisor . x < = 0.0f ? 0.0f : clamp ( burnt . x , 0.0f , 1.0f ) ,
divisor . y < = 0.0f ? 0.0f : clamp ( burnt . y , 0.0f , 1.0f ) ,
divisor . z < = 0.0f ? 0.0f : clamp ( burnt . z , 0.0f , 1.0f ) ) ;
}
/// <summary>RGB to HSV as Cycles computes it (util/color.h rgb_to_hsv): hue in [0, 1), saturation, value.</summary>
public static float3 cycles_rgb_to_hsv ( float3 rgb )
{
var cmax = max ( rgb . x , max ( rgb . y , rgb . z ) ) ;
var cmin = min ( rgb . x , min ( rgb . y , rgb . z ) ) ;
var cdelta = cmax - cmin ;
var s = cmax ! = 0.0f ? cdelta / cmax : 0.0f ;
var c = ( new float3 ( cmax , cmax , cmax ) - rgb ) / cdelta ;
var h = rgb . x = = cmax ? c . z - c . y : ( rgb . y = = cmax ? 2.0f + c . x - c . z : 4.0f + c . y - c . x ) ;
h = h / 6.0f ;
h = h < 0.0f ? h + 1.0f : h ;
return new float3 ( s ! = 0.0f ? h : 0.0f , s , cmax ) ;
}
/// <summary>HSV to RGB as Cycles computes it (util/color.h hsv_to_rgb).</summary>
public static float3 cycles_hsv_to_rgb ( float3 hsv )
{
var h = hsv . x = = 1.0f ? 0.0f : hsv . x ;
h = h * 6.0f ;
var i = floor ( h ) ;
var f = h - i ;
var p = hsv . z * ( 1.0f - hsv . y ) ;
var q = hsv . z * ( 1.0f - ( hsv . y * f ) ) ;
var t = hsv . z * ( 1.0f - ( hsv . y * ( 1.0f - f ) ) ) ;
var rgb = i = = 0.0f ? new float3 ( hsv . z , t , p ) :
( i = = 1.0f ? new float3 ( q , hsv . z , p ) :
( i = = 2.0f ? new float3 ( p , hsv . z , t ) :
( i = = 3.0f ? new float3 ( p , q , hsv . z ) :
( i = = 4.0f ? new float3 ( t , p , hsv . z ) : new float3 ( hsv . z , p , q ) ) ) ) ) ;
return hsv . y ! = 0.0f ? rgb : new float3 ( hsv . z , hsv . z , hsv . z ) ;
}
/// <summary>Mix node, Hue blend mode (svm_mix_hue).</summary>
public static float3 cycles_mix_hue ( float t , float3 col1 , float3 col2 )
{
var hsv2 = cycles_rgb_to_hsv ( col2 ) ;
var hsv = cycles_rgb_to_hsv ( col1 ) ;
var shifted = cycles_hsv_to_rgb ( new float3 ( hsv2 . x , hsv . y , hsv . z ) ) ;
return hsv2 . y ! = 0.0f ? col1 + t * ( shifted - col1 ) : col1 ;
}
/// <summary>Mix node, Saturation blend mode (svm_mix_sat).</summary>
public static float3 cycles_mix_sat ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
var hsv = cycles_rgb_to_hsv ( col1 ) ;
var hsv2 = cycles_rgb_to_hsv ( col2 ) ;
return hsv . y ! = 0.0f ? cycles_hsv_to_rgb ( new float3 ( hsv . x , tm * hsv . y + t * hsv2 . y , hsv . z ) ) : col1 ;
}
/// <summary>Mix node, Value blend mode (svm_mix_val).</summary>
public static float3 cycles_mix_val ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
var hsv = cycles_rgb_to_hsv ( col1 ) ;
var hsv2 = cycles_rgb_to_hsv ( col2 ) ;
return cycles_hsv_to_rgb ( new float3 ( hsv . x , hsv . y , tm * hsv . z + t * hsv2 . z ) ) ;
}
/// <summary>Mix node, Color blend mode (svm_mix_color).</summary>
public static float3 cycles_mix_color ( float t , float3 col1 , float3 col2 )
{
var hsv2 = cycles_rgb_to_hsv ( col2 ) ;
var hsv = cycles_rgb_to_hsv ( col1 ) ;
var shifted = cycles_hsv_to_rgb ( new float3 ( hsv2 . x , hsv2 . y , hsv . z ) ) ;
return hsv2 . y ! = 0.0f ? col1 + t * ( shifted - col1 ) : col1 ;
}
/// <summary>Mix node, Soft Light blend mode (svm_mix_soft).</summary>
public static float3 cycles_mix_soft ( float t , float3 col1 , float3 col2 )
{
var tm = 1.0f - t ;
var one = new float3 ( 1.0f , 1.0f , 1.0f ) ;
var scr = one - ( one - col2 ) * ( one - col1 ) ;
return tm * col1 + t * ( ( one - col1 ) * col2 * col1 + col1 * scr ) ;
}
/// <summary>Mix node, Linear Light blend mode (svm_mix_linear).</summary>
public static float3 cycles_mix_linear ( float t , float3 col1 , float3 col2 ) = >
col1 + t * ( 2.0f * col2 + new float3 ( - 1.0f , - 1.0f , - 1.0f ) ) ;
// ---- RGB to BW node (kernel/svm/convert.h NODE_CONVERT_CF) ----
/// <summary>RGB to BW node: the scene-linear luminance of a colour.</summary>
public static float cycles_rgb_to_bw ( float3 color ) = >
color . x * CYCLES_RGB_TO_Y_R + color . y * CYCLES_RGB_TO_Y_G + color . z * CYCLES_RGB_TO_Y_B ;
// ---- Color Ramp lookup (kernel/svm/ramp.h rgb_ramp_lookup, without extrapolation) ----
/// <summary>
/// Where a Color Ramp of `size` table entries is read at t: (index, weight), the lower entry and the
/// weight of the next one. The colour is table[index] when weight is 0 and
/// (1 - weight) table[index] + weight table[index + 1] otherwise. The ramp table itself is the caller's.
/// </summary>
public static float2 cycles_ramp_index ( float t , int size )
{
2026-10-12 04:07:08 +02:00
var f = ( t > 0.0f ? ( t < 1.0f ? t : 1.0f ) : 0.0f ) * ( float ) ( size - 1 ) ;
CultMath: Cycles node semantics (Math, Map Range, Clamp, Mix, RGB to BW, ramp index) as math.Cycles.cs and CultMath.Cycles.hlsl under Apache-2.0
Ports of Blender v5.2.2's intern/cycles kernel functions, one function per node
operation, with the HLSL mirror, the Unity copy, the lowering table entry for a
separate Apache-2.0 GLSL file, the golden-fixture families, the package licence
expressions and the notices section.
Builds. Does not pass yet: CultMath.Cycles.glsl and the regenerated golden
fixture arrive in a later commit, so CommittedGlslEqualsLowering and
GoldenFixtureMatchesCSharp fail at this commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 03:59:59 +02:00
var index = clamp ( ( int ) f , 0 , size - 1 ) ;
2026-10-12 04:07:08 +02:00
var lower = ( float ) index ;
return new float2 ( lower , f - lower ) ;
CultMath: Cycles node semantics (Math, Map Range, Clamp, Mix, RGB to BW, ramp index) as math.Cycles.cs and CultMath.Cycles.hlsl under Apache-2.0
Ports of Blender v5.2.2's intern/cycles kernel functions, one function per node
operation, with the HLSL mirror, the Unity copy, the lowering table entry for a
separate Apache-2.0 GLSL file, the golden-fixture families, the package licence
expressions and the notices section.
Builds. Does not pass yet: CultMath.Cycles.glsl and the regenerated golden
fixture arrive in a later commit, so CommittedGlslEqualsLowering and
GoldenFixtureMatchesCSharp fail at this commit.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 03:59:59 +02:00
}
}