cultmath: span_rounding and span_slack own S_o and the span allowances (C#, HLSL, Unity copy)
The inline rounding and the three allowance literals of span_stage and cultmath_span_stage move into internal span_rounding / span_slack and their HLSL twins; operations and order are unchanged. CultMath.glsl is not regenerated in this commit, so GlslMirrorTests.CommittedGlslEqualsLowering fails here; it compiles. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a30cb7d254
commit
2478a36a22
3 changed files with 65 additions and 24 deletions
|
|
@ -610,7 +610,8 @@ CultCellular cultmath_cellular(float3 p)
|
|||
// mirror. They follow the includes because the bounds read CULTMATH_SNOISE_LIPSCHITZ, _HESSIAN and _THIRD.
|
||||
// A shader caller builds a SpanStage itself (SpanStage(1.0, 1.0, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0))
|
||||
// is the identity of math.SpanStage.Identity, which has no shader form); see math.span_stage's comments for
|
||||
// the span, the transform, the bounds and the allowances.
|
||||
// the span, the transform, the bounds and the allowances. cultmath_span_rounding and cultmath_span_slack
|
||||
// are math.span_rounding and math.span_slack, the owners of S_o and of the three allowances.
|
||||
struct SpanPath
|
||||
{
|
||||
float3 midpoint;
|
||||
|
|
@ -637,6 +638,18 @@ struct SpanNoise
|
|||
float fold;
|
||||
};
|
||||
|
||||
float cultmath_span_rounding(float scale, float3 mq, SpanStage stage, float3 octaveShift, float nu, float halfLength)
|
||||
{
|
||||
return 9.5367431640625e-7 * (abs(scale) * (cultmath_csum(abs(mq)) + cultmath_csum(abs(stage.shift))) + cultmath_csum(abs(stage.offset)) + cultmath_csum(abs(octaveShift)) + 1.7320508 * nu * halfLength);
|
||||
}
|
||||
|
||||
float3 cultmath_span_slack(float rounding)
|
||||
{
|
||||
return float3(6.103515625e-5 + CULTMATH_SNOISE_LIPSCHITZ * rounding,
|
||||
2.44140625e-4 + CULTMATH_SNOISE_HESSIAN * rounding,
|
||||
9.765625e-4 + CULTMATH_SNOISE_THIRD * rounding);
|
||||
}
|
||||
|
||||
// The octave stage behind cultmath_span_fbm and cultmath_span_ridged, as math.span_stage is behind span_fbm
|
||||
// and span_ridged. The Frobenius norm of the Hessian is written out in C#'s order (GLSL has no _mRC member
|
||||
// access); the C# local named step is octaveShift here (it would shadow GLSL's step()).
|
||||
|
|
@ -679,10 +692,11 @@ SpanNoise cultmath_span_stage(bool ridged, SpanPath path, SpanStage stage, int o
|
|||
float weight = abs(amplitude);
|
||||
float factor = abs(scale) * norm;
|
||||
float nu = factor * path.speed;
|
||||
float rounding = 9.5367431640625e-7 * (abs(scale) * (cultmath_csum(abs(mq)) + cultmath_csum(abs(stage.shift))) + cultmath_csum(abs(stage.offset)) + cultmath_csum(abs(octaveShift)) + 1.7320508 * nu * halfLength);
|
||||
float valueSlack = 6.103515625e-5 + CULTMATH_SNOISE_LIPSCHITZ * rounding;
|
||||
float gradientSlack = 2.44140625e-4 + CULTMATH_SNOISE_HESSIAN * rounding;
|
||||
float hessianSlack = 9.765625e-4 + CULTMATH_SNOISE_THIRD * rounding;
|
||||
float rounding = cultmath_span_rounding(scale, mq, stage, octaveShift, nu, halfLength);
|
||||
float3 slack = cultmath_span_slack(rounding);
|
||||
float valueSlack = slack.x;
|
||||
float gradientSlack = slack.y;
|
||||
float hessianSlack = slack.z;
|
||||
float3x3 hm = cultmath_snoise_hessian(x);
|
||||
float frobenius = sqrt(hm[0][0] * hm[0][0] + hm[0][1] * hm[0][1] + hm[0][2] * hm[0][2] + hm[1][0] * hm[1][0] + hm[1][1] * hm[1][1] + hm[1][2] * hm[1][2]
|
||||
+ hm[2][0] * hm[2][0] + hm[2][1] * hm[2][1] + hm[2][2] * hm[2][2]);
|
||||
|
|
|
|||
|
|
@ -151,6 +151,20 @@ public static partial class math
|
|||
public static SpanNoise span_ridged(SpanPath path, SpanStage stage, int octaves, float lacunarity, float gain) =>
|
||||
span_stage(true, path, stage, octaves, lacunarity, gain);
|
||||
|
||||
// 2^-20 S_o: the rounding one octave's read of the noise carries, S_o being the sum of the magnitudes the
|
||||
// octave's coordinate is formed from and the span's reach (the allowance paragraph above). The single
|
||||
// owner of S_o; span_slack turns it into the three allowances.
|
||||
internal static float span_rounding(float scale, float3 mq, SpanStage stage, float3 step, float nu, float halfLength) =>
|
||||
9.5367431640625e-7f * (abs(scale) * (csum(abs(mq)) + csum(abs(stage.shift))) + csum(abs(stage.offset)) + csum(abs(step)) + 1.7320508f * nu * halfLength);
|
||||
|
||||
// The allowances (e_n, e_g, e_h) of the value, the gradient's length and the Hessian's Frobenius norm at
|
||||
// a rounding of 2^-20 S_o: the absolute term of each plus its derivative's pin times the rounding. The
|
||||
// single owner of the three; nothing else computes an allowance.
|
||||
internal static float3 span_slack(float rounding) => new(
|
||||
6.103515625e-5f + SNOISE_LIPSCHITZ * rounding,
|
||||
2.44140625e-4f + SNOISE_HESSIAN * rounding,
|
||||
9.765625e-4f + SNOISE_THIRD * rounding);
|
||||
|
||||
// The span is SpanPath: q(t), |t| <= halfLength, through midpoint with velocity at t = 0, |q'| <= speed
|
||||
// and |q''| <= acceleration along it (speed at least |velocity|; all three and halfLength non-negative).
|
||||
// The transform is SpanStage: octave o samples snoise_grad at x_o(q) = f_o (M q + shift) + offset +
|
||||
|
|
@ -182,16 +196,14 @@ public static partial class math
|
|||
// coordinate by about 2u of the magnitudes it is formed from, which moves a result by its derivative
|
||||
// times that, and the caller's scaling of the point and a GPU's evaluation take 2u to 16u = 2^-20.
|
||||
// S_o is the sum of those magnitudes, |f_o| (|M q|_1 + |shift|_1) + |offset|_1 + |o octaveStep|_1, plus
|
||||
// the span's reach sqrt(3) nu halfLength (the coordinate of a path point). The allowances are the
|
||||
// value's e_n = 2^-14 + L 2^-20 S_o, the gradient length's e_g = 2^-12 + H 2^-20 S_o (H is the
|
||||
// gradient's own derivative's pin) and the Hessian's Frobenius norm's e_h = 2^-10 + T 2^-20 S_o (T is
|
||||
// the Hessian's derivative's pin): af_snoise's absolute terms for the first two, and 2^-10 for the
|
||||
// Hessian's, which the kernel's rounding at small coordinates (2.3e-5 absolute, measured against a
|
||||
// double kernel) sits well under. Two samples of that probe (6000 points per band, |x| from 2^-2 to
|
||||
// 2^23) observed the error's maxima, as fractions of e_n, e_g and e_h: 0.057, 0.046 and 0.075 in the
|
||||
// cut's own sample (seed not recorded; the 0.075 is against T 2^-20 |x|_1 alone, at the smallest
|
||||
// band), and 0.064, 0.0485 and 0.057 in an audit's (seed 0x501). They are samples, not bounds: a new
|
||||
// seed moves them. What they say is that the allowances enclose the error with a margin of about 15.
|
||||
// the span's reach sqrt(3) nu halfLength (the coordinate of a path point); span_rounding is its owner. The
|
||||
// allowances are the value's e_n = 2^-14 + L 2^-20 S_o, the gradient length's e_g = 2^-12 + H 2^-20 S_o
|
||||
// (H is the gradient's own derivative's pin) and the Hessian's Frobenius norm's e_h = 2^-10 + T 2^-20 S_o
|
||||
// (T is the Hessian's derivative's pin): af_snoise's absolute terms for the first two, and 2^-10 for the
|
||||
// Hessian's; span_slack is their owner. They are observed where they are decided: the coordinate-band
|
||||
// snoise_grad and snoise_hessian families of glsl-parity.json and SpanAllowanceCoversReadsAlongTheSpan
|
||||
// check every read against them. Those observations are samples, not bounds: a new seed moves them. What
|
||||
// they say is that the allowances enclose the error with a margin of about 15.
|
||||
// No new pin enters.
|
||||
//
|
||||
// Domain: no coordinate range is checked, clamped or refused. The allowances grow with S_o, so
|
||||
|
|
@ -237,10 +249,11 @@ public static partial class math
|
|||
var weight = abs(amplitude);
|
||||
var factor = abs(scale) * norm;
|
||||
var nu = factor * path.speed;
|
||||
var rounding = 9.5367431640625e-7f * (abs(scale) * (csum(abs(mq)) + csum(abs(stage.shift))) + csum(abs(stage.offset)) + csum(abs(step)) + 1.7320508f * nu * halfLength);
|
||||
var valueSlack = 6.103515625e-5f + SNOISE_LIPSCHITZ * rounding;
|
||||
var gradientSlack = 2.44140625e-4f + SNOISE_HESSIAN * rounding;
|
||||
var hessianSlack = 9.765625e-4f + SNOISE_THIRD * rounding;
|
||||
var rounding = span_rounding(scale, mq, stage, step, nu, halfLength);
|
||||
var slack = span_slack(rounding);
|
||||
var valueSlack = slack.x;
|
||||
var gradientSlack = slack.y;
|
||||
var hessianSlack = slack.z;
|
||||
var local = min(SNOISE_HESSIAN, span_frobenius(snoise_hessian(x)) + hessianSlack + SNOISE_THIRD * nu * halfLength);
|
||||
curvature += weight * factor * factor * local;
|
||||
if (ridged)
|
||||
|
|
|
|||
|
|
@ -610,7 +610,8 @@ CultCellular cultmath_cellular(float3 p)
|
|||
// mirror. They follow the includes because the bounds read CULTMATH_SNOISE_LIPSCHITZ, _HESSIAN and _THIRD.
|
||||
// A shader caller builds a SpanStage itself (SpanStage(1.0, 1.0, vec3(0.0), vec3(0.0), vec3(0.0), vec3(0.0))
|
||||
// is the identity of math.SpanStage.Identity, which has no shader form); see math.span_stage's comments for
|
||||
// the span, the transform, the bounds and the allowances.
|
||||
// the span, the transform, the bounds and the allowances. cultmath_span_rounding and cultmath_span_slack
|
||||
// are math.span_rounding and math.span_slack, the owners of S_o and of the three allowances.
|
||||
struct SpanPath
|
||||
{
|
||||
float3 midpoint;
|
||||
|
|
@ -637,6 +638,18 @@ struct SpanNoise
|
|||
float fold;
|
||||
};
|
||||
|
||||
float cultmath_span_rounding(float scale, float3 mq, SpanStage stage, float3 octaveShift, float nu, float halfLength)
|
||||
{
|
||||
return 9.5367431640625e-7 * (abs(scale) * (cultmath_csum(abs(mq)) + cultmath_csum(abs(stage.shift))) + cultmath_csum(abs(stage.offset)) + cultmath_csum(abs(octaveShift)) + 1.7320508 * nu * halfLength);
|
||||
}
|
||||
|
||||
float3 cultmath_span_slack(float rounding)
|
||||
{
|
||||
return float3(6.103515625e-5 + CULTMATH_SNOISE_LIPSCHITZ * rounding,
|
||||
2.44140625e-4 + CULTMATH_SNOISE_HESSIAN * rounding,
|
||||
9.765625e-4 + CULTMATH_SNOISE_THIRD * rounding);
|
||||
}
|
||||
|
||||
// The octave stage behind cultmath_span_fbm and cultmath_span_ridged, as math.span_stage is behind span_fbm
|
||||
// and span_ridged. The Frobenius norm of the Hessian is written out in C#'s order (GLSL has no _mRC member
|
||||
// access); the C# local named step is octaveShift here (it would shadow GLSL's step()).
|
||||
|
|
@ -679,10 +692,11 @@ SpanNoise cultmath_span_stage(bool ridged, SpanPath path, SpanStage stage, int o
|
|||
float weight = abs(amplitude);
|
||||
float factor = abs(scale) * norm;
|
||||
float nu = factor * path.speed;
|
||||
float rounding = 9.5367431640625e-7 * (abs(scale) * (cultmath_csum(abs(mq)) + cultmath_csum(abs(stage.shift))) + cultmath_csum(abs(stage.offset)) + cultmath_csum(abs(octaveShift)) + 1.7320508 * nu * halfLength);
|
||||
float valueSlack = 6.103515625e-5 + CULTMATH_SNOISE_LIPSCHITZ * rounding;
|
||||
float gradientSlack = 2.44140625e-4 + CULTMATH_SNOISE_HESSIAN * rounding;
|
||||
float hessianSlack = 9.765625e-4 + CULTMATH_SNOISE_THIRD * rounding;
|
||||
float rounding = cultmath_span_rounding(scale, mq, stage, octaveShift, nu, halfLength);
|
||||
float3 slack = cultmath_span_slack(rounding);
|
||||
float valueSlack = slack.x;
|
||||
float gradientSlack = slack.y;
|
||||
float hessianSlack = slack.z;
|
||||
float3x3 hm = cultmath_snoise_hessian(x);
|
||||
float frobenius = sqrt(hm[0][0] * hm[0][0] + hm[0][1] * hm[0][1] + hm[0][2] * hm[0][2] + hm[1][0] * hm[1][0] + hm[1][1] * hm[1][1] + hm[1][2] * hm[1][2]
|
||||
+ hm[2][0] * hm[2][0] + hm[2][1] * hm[2][1] + hm[2][2] * hm[2][2]);
|
||||
|
|
|
|||
Loading…
Reference in a new issue