fp64 hyperbolic navigation

fp64 hyperbolic navigation

LORAN 방식의 해도 격자.

// ═══ typeshade example — fp64 hyperbolic navigation (LORAN) ═══
//
// Hyperbolic radio navigation, the pre-GPS chart grid: two stations, and your
// position line is "d₁ − d₂ = const" — a hyperbola. The catch for GPU floats:
// the observer is ~10⁷ units from both stations, the usable signal is the
// DIFFERENCE of two nearly-equal distances (catastrophic cancellation), and
// the band phase needs fract() of a coordinate-scale value — three classic
// f32 killers in one formula. The f64 side runs the whole chain through the
// vec64 `distance` reduction (extended-precision accumulation) and a df64
// fract; the plain-f32 left half renders quantized band garbage at ANY zoom
// (CPU-verified: 17 clean band values vs 6 garbage ones per 48² window).
//
// The `_fp64` guard uniform is auto-injected by the lowering; the render
// harnesses bind it to 1.0f by probing the program for the Fp64Guard block.
import {
fn,
module,
vec2,
vec3,
vec4,
f32,
pow,
fract,
min,
mix,
length,
distance,
smoothstep,
fwidth,
toF32,
toF64,
f32T,
vec2fT,
vec2f64T,
vec2f64,
Let,
uniformStruct,
} from '../src/index.js';
import { VsOut, vs } from './_fullscreen.js';
import type { ShaderExample } from './_shared.js';
// The observer and both stations are host-supplied df64 uniforms swept out
// from the origin together via the DISTANCE slider (see controls below), so
// there are no hard-coded station coordinates any more — only the band pitches.
const LAMBDA_H = 4; // hyperbolic band spacing (world units of d₁−d₂)
const LAMBDA_E = 16; // elliptic band spacing (world units of d₁+d₂)
const U = uniformStruct(
'Uniforms',
{ group: 0, binding: 0, as: 'u' },
{
center: vec2f64T, // observer — one DF64Vec2 slot [hi.x, hi.y, lo.x, lo.y]
st_a: vec2f64T, // master station (swept with the observer via the DISTANCE slider)
st_b: vec2f64T, // secondary station
resolution: vec2fT,
zoom_exp: f32T, // view span = 10^-zoom_exp world units (negative = zoom out)
fp64: f32T, // toggle: 1 = split-screen f32 | f64 (canonical), 0 = all-f32
},
);
const fsLoran = fn(
'fs_loran',
{ vo: VsOut },
(p) => {
const span = Let(pow(f32(10.0), U.field.zoom_exp.neg()));
const half = Let(p.vo.uv.x.mul(2.0));
const sx = Let(half.sub(p.vo.uv.x.lt(0.5).select(0.0, 1.0)));
const dx = Let(sx.sub(0.5).mul(span));
const dy = Let(
p.vo.uv.y.sub(0.5).mul(span).mul(U.field.resolution.y.div(U.field.resolution.x).mul(2.0)),
);
const isF32 = Let(p.vo.uv.x.lt(0.5).or(U.field.fp64.lt(0.5)));
// f64 chain — vec64 distance accumulates through the scalar df64 chain,
// the subtraction cancels EXACTLY, and only the band phase narrows.
const pos = Let(vec2f64(U.field.center.x.add(toF64(dx)), U.field.center.y.add(toF64(dy))));
const d1 = Let(distance(pos, U.field.st_a));
const d2 = Let(distance(pos, U.field.st_b));
const th64 = Let(toF32(fract(d1.sub(d2).mul(1 / LAMBDA_H))));
const te64 = Let(toF32(fract(d1.add(d2).mul(1 / LAMBDA_E))));
// f32 twin — SAME formulas, everything narrowed first: once the coordinate
// ulp exceeds a band, d₁, d₂ quantize and the band phase becomes garbage.
const pos32 = Let(vec2(toF32(U.field.center.x).add(dx), toF32(U.field.center.y).add(dy)));
const d1f = Let(length(pos32.sub(vec2(toF32(U.field.st_a.x), toF32(U.field.st_a.y)))));
const d2f = Let(length(pos32.sub(vec2(toF32(U.field.st_b.x), toF32(U.field.st_b.y)))));
const th32 = Let(fract(d1f.sub(d2f).mul(1 / LAMBDA_H)));
const te32 = Let(fract(d1f.add(d2f).mul(1 / LAMBDA_E)));
const th = Let(isF32.select(th32, th64));
const te = Let(isF32.select(te32, te64));
// Distance to the nearest band line (triangle fold — continuous across the
// fract seam, which is exactly where the line sits).
const dh = Let(min(th, f32(1).sub(th)));
const de = Let(min(te, f32(1).sub(te)));
const aaH = Let(fwidth(dh).mul(1.2).add(1e-4));
const aaE = Let(fwidth(de).mul(1.2).add(1e-4));
const lineH = Let(f32(1).sub(smoothstep(f32(0), aaH, dh)));
const lineE = Let(f32(1).sub(smoothstep(f32(0), aaE, de)));
// Chart styling: deep sea, cyan hyperbolae (the position lines), faint
// amber ellipses (the range net), a soft band tint to keep the field alive.
const sea = Let(mix(vec3(0.02, 0.07, 0.13), vec3(0.04, 0.12, 0.2), p.vo.uv.y));
const rgb = sea
.add(vec3(0.0, 0.06, 0.08).mul(th)) // band tint
.add(vec3(0.25, 0.95, 0.95).mul(lineH.mul(0.9)))
.add(vec3(0.95, 0.7, 0.25).mul(lineE.mul(0.35)));
return vec4(rgb, f32(1));
},
{ stage: 'fragment', retAttr: '@location(0)' },
);
// `_fp64` guard lands at (group 0, binding 1) automatically.
const fp64LoranModule = module({
funcs: [vs, fsLoran],
uses: [U, VsOut],
});
export const fp64Loran: ShaderExample = {
id: 'fp64-loran',
title: 'fp64 hyperbolic navigation',
blurb:
'A LORAN-style chart grid: cyan hyperbolae of constant d₁−d₂ to two stations, amber ellipses of constant d₁+d₂. The signal is the DIFFERENCE of two nearly-equal distances followed by fract() — catastrophic cancellation plus phase recovery. Drag the DISTANCE slider to push the whole station triangle out from the origin: near 10⁶ both halves are a clean chart, but past ~10⁷·² the coordinate ulp grows wider than a band, d₁ and d₂ quantize, and the plain-f32 left half dissolves into blocky garbage — while the vec64 distance reduction keeps the right half sharp to 10⁹. Wheel drives the distance; flip the fp64 toggle to compare.',
category: 'cartographic',
file: 'fp64-loran.ts',
module: fp64LoranModule,
renderable: true,
splitLabels: ['f32', 'f64 (emulated)'],
controls: {
// Observer and both stations sweep together: coord = base·10^mag (+offset for
// the observer's sub-unit detail). At mag = 7 this is the classic ~10⁷ view.
center: { kind: 'logmag2d', magField: 'mag', base: [2.31, 3.07], offset: [0.13, 0.57] },
st_a: { kind: 'logmag2d', magField: 'mag', base: [1.2, 3.4], offset: [0, 0] },
st_b: { kind: 'logmag2d', magField: 'mag', base: [1.9, 2.6], offset: [0, 0] },
resolution: { kind: 'resolution' },
mag: {
kind: 'slider',
label: 'Distance from origin 10^x',
min: 4,
max: 9,
step: 0.02,
value: 6,
wheel: true,
},
// View span across each half (10^-zoom_exp world units ≈ number of bands).
zoom_exp: { kind: 'slider', label: 'Zoom 10^-x', min: -3.0, max: 3.0, step: 0.05, value: -1.6 },
fp64: { kind: 'toggle', label: 'fp64 emulation', value: true },
},
};
struct Uniforms {
@align(16) center: DF64Vec2,
@align(16) st_a: DF64Vec2,
@align(16) st_b: DF64Vec2,
resolution: vec2<f32>,
zoom_exp: f32,
fp64: f32,
}
struct VsOut {
@builtin(position) pos: vec4<f32>,
@location(0) uv: vec2<f32>,
}
struct DF64Vec2 {
hi: vec2<f32>,
lo: vec2<f32>,
}
@group(0) @binding(0) var<uniform> u: Uniforms;
@group(0) @binding(1) var _fp64: texture_2d<f32>;
@vertex
fn vs(@builtin(vertex_index) vi: u32) -> VsOut {
let _cse0 = ((f32((vi & 1u)) * 4.0) - 1.0);
let _cse1 = ((f32((vi >> 1u)) * 4.0) - 1.0);
return VsOut(vec4<f32>(_cse0, _cse1, 0.0, 1.0), vec2<f32>(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5)));
}
@fragment
fn fs_loran(vo: VsOut) -> @location(0) vec4<f32> {
let _fp64_g = textureLoad(_fp64, vec2<i32>(0, 0), 0).x;
let _v0 = pow(10.0, (-u.zoom_exp));
let _v1 = (vo.uv.x * 2.0);
let _cse0 = (vo.uv.x < 0.5);
let _v2 = (_v1 - select(1.0, 0.0, _cse0));
let _v3 = ((_v2 - 0.5) * _v0);
let _v4 = (((vo.uv.y - 0.5) * _v0) * ((u.resolution.y / u.resolution.x) * 2.0));
let _v5 = (_cse0 || (u.fp64 < 0.5));
let _cse1 = vec2<f32>(u.center.hi.x, u.center.lo.x);
let _cse2 = vec2<f32>(u.center.hi.y, u.center.lo.y);
let _lc0 = df64_add(_cse1, vec2<f32>(_v3, 0.0), _fp64_g);
let _lc1 = df64_add(_cse2, vec2<f32>(_v4, 0.0), _fp64_g);
let _v6 = DF64Vec2(vec2<f32>(_lc0.x, _lc1.x), vec2<f32>(_lc0.y, _lc1.y));
let _cse8 = bitcast<f32>(bitcast<u32>(0.0));
let _cse3 = vec2<f32>(_cse8, _cse8);
let _cse4 = vec2<f32>(u.st_a.hi.x, u.st_a.lo.x);
let _cse5 = vec2<f32>(u.st_a.hi.y, u.st_a.lo.y);
let _gv0 = df64_add(vec2<f32>(_v6.hi.x, _v6.lo.x), _cse3, _fp64_g);
let _gv1 = df64_add(vec2<f32>(_v6.hi.y, _v6.lo.y), _cse3, _fp64_g);
let _v7 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse4, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse5, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
let _cse6 = vec2<f32>(u.st_b.hi.x, u.st_b.lo.x);
let _cse7 = vec2<f32>(u.st_b.hi.y, u.st_b.lo.y);
let _v8 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse6, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse7, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
let _v9 = df64_narrow(df64_fract((df64_sub(df64_add(_v7, _cse3, _fp64_g), df64_add(_v8, _cse3, _fp64_g), _fp64_g) * 0.25), _fp64_g));
let _v10 = df64_narrow(df64_fract((df64_add(_v7, _v8, _fp64_g) * 0.0625), _fp64_g));
let _v11 = vec2<f32>((df64_narrow(_cse1) + _v3), (df64_narrow(_cse2) + _v4));
let _v12 = length((_v11 - vec2<f32>(df64_narrow(_cse4), df64_narrow(_cse5))));
let _v13 = length((_v11 - vec2<f32>(df64_narrow(_cse6), df64_narrow(_cse7))));
let _v14 = fract(((_v12 - _v13) * 0.25));
let _v15 = fract(((_v12 + _v13) * 0.0625));
let _v16 = select(_v9, _v14, _v5);
let _v17 = select(_v10, _v15, _v5);
let _v18 = min(_v16, (1.0 - _v16));
let _v19 = min(_v17, (1.0 - _v17));
let _v20 = ((fwidth(_v18) * 1.2) + 0.0001);
let _v21 = ((fwidth(_v19) * 1.2) + 0.0001);
let _v22 = (1.0 - smoothstep(0.0, _v20, _v18));
let _v23 = (1.0 - smoothstep(0.0, _v21, _v19));
let _v24 = mix(vec3<f32>(0.02, 0.07, 0.13), vec3<f32>(0.04, 0.12, 0.2), vo.uv.y);
return vec4<f32>((((_v24 + (vec3<f32>(0.0, 0.06, 0.08) * _v16)) + (vec3<f32>(0.25, 0.95, 0.95) * (_v22 * 0.9))) + (vec3<f32>(0.95, 0.7, 0.25) * (_v23 * 0.35))), 1.0);
}
fn df64_twoSum(a: f32, b: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a + b);
let _v1 = (((_v0 * _fp64_g) - a) * _fp64_g);
let _v2 = (((a - ((_v0 - _v1) * _fp64_g)) * _fp64_g) + (b - _v1));
return vec2<f32>(_v0, _v2);
}
fn df64_quickTwoSum(a: f32, b: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = ((a + b) * _fp64_g);
let _v1 = (b - ((_v0 - a) * _fp64_g));
return vec2<f32>(_v0, _v1);
}
fn df64_split(a: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a * (_fp64_g * 4097.0));
let _v1 = ((_v0 * _fp64_g) - (_v0 - a));
let _v2 = ((a * _fp64_g) - _v1);
return vec2<f32>(_v1, _v2);
}
fn df64_twoProd(a: f32, b: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a * b);
let _v1 = df64_split(a, _fp64_g);
let _v2 = df64_split(b, _fp64_g);
let _v3 = (((((_v1.x * _v2.x) - _v0) + (_v1.x * _v2.y)) + (_v1.y * _v2.x)) + (_v1.y * _v2.y));
return vec2<f32>(_v0, _v3);
}
fn df64_twoSqr(a: f32, _fp64_g: f32) -> vec2<f32> {
let _v0 = (a * a);
let _v1 = df64_split(a, _fp64_g);
let _v2 = (((((_v1.x * _v1.x) - _v0) * _fp64_g) + (((_v1.x * _v1.y) * 2.0) * _fp64_g)) + ((_v1.y * _v1.y) * _fp64_g));
return vec2<f32>(_v0, _v2);
}
fn df64_add(a: vec2<f32>, b: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
var _v0: vec2<f32> = df64_twoSum(a.x, b.x, _fp64_g);
let _v1 = df64_twoSum(a.y, b.y, _fp64_g);
_v0.y = (_v0.y + _v1.x);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
_v0.y = (_v0.y + _v1.y);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
return _v0;
}
fn df64_sub(a: vec2<f32>, b: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
return df64_add(a, (-b), _fp64_g);
}
fn df64_sqr(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
var _v0: vec2<f32> = df64_twoSqr(a.x, _fp64_g);
_v0.y = (_v0.y + ((a.x * a.y) * 2.0));
return df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
}
fn df64_sqrt(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
let _v0 = (_fp64_g / sqrt(a.x));
let _v1 = (a.x * _v0);
let _v2 = (df64_twoSqr(_v1, _fp64_g) * _fp64_g);
let _v3 = df64_sub(a, _v2, _fp64_g).x;
let _v4 = df64_twoProd((_v0 * 0.5), _v3, _fp64_g);
let _v5 = df64_add(vec2<f32>(_v1, 0.0), _v4, _fp64_g);
return select(_v5, vec2<f32>(0.0, 0.0), (a.x == 0.0));
}
fn df64_floor(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
let _v0 = floor(a.x);
return select(vec2<f32>(_v0, 0.0), df64_quickTwoSum(_v0, floor(a.y), _fp64_g), (_v0 == a.x));
}
fn df64_fract(a: vec2<f32>, _fp64_g: f32) -> vec2<f32> {
return df64_sub(a, df64_floor(a, _fp64_g), _fp64_g);
}
fn df64_narrow(a: vec2<f32>) -> f32 {
return (a.x + a.y);
}
#version 300 es
precision highp float;
precision highp int;
out vec2 uv;
void main() {
uint vi = uint(gl_VertexID);
float _cse0 = ((float((vi & 1u)) * 4.0) - 1.0);
float _cse1 = ((float((vi >> 1u)) * 4.0) - 1.0);
gl_Position = vec4(_cse0, _cse1, 0.0, 1.0);
uv = vec2(((_cse0 * 0.5) + 0.5), ((_cse1 * 0.5) + 0.5));
}
#version 300 es
precision highp float;
precision highp int;
struct DF64Vec2 {
vec2 hi;
vec2 lo;
};
layout(std140) uniform Uniforms {
DF64Vec2 center;
DF64Vec2 st_a;
DF64Vec2 st_b;
vec2 resolution;
float zoom_exp;
float fp64;
} u;
uniform highp sampler2D _fp64;
vec2 df64_twoSum(float a, float b, float _fp64_g) {
float _v0 = (a + b);
float _v1 = (((_v0 * _fp64_g) - a) * _fp64_g);
float _v2 = (((a - ((_v0 - _v1) * _fp64_g)) * _fp64_g) + (b - _v1));
return vec2(_v0, _v2);
}
vec2 df64_quickTwoSum(float a, float b, float _fp64_g) {
float _v0 = ((a + b) * _fp64_g);
float _v1 = (b - ((_v0 - a) * _fp64_g));
return vec2(_v0, _v1);
}
vec2 df64_split(float a, float _fp64_g) {
float _v0 = (a * (_fp64_g * 4097.0));
float _v1 = ((_v0 * _fp64_g) - (_v0 - a));
float _v2 = ((a * _fp64_g) - _v1);
return vec2(_v1, _v2);
}
vec2 df64_twoProd(float a, float b, float _fp64_g) {
float _v0 = (a * b);
vec2 _v1 = df64_split(a, _fp64_g);
vec2 _v2 = df64_split(b, _fp64_g);
float _v3 = (((((_v1.x * _v2.x) - _v0) + (_v1.x * _v2.y)) + (_v1.y * _v2.x)) + (_v1.y * _v2.y));
return vec2(_v0, _v3);
}
vec2 df64_twoSqr(float a, float _fp64_g) {
float _v0 = (a * a);
vec2 _v1 = df64_split(a, _fp64_g);
float _v2 = (((((_v1.x * _v1.x) - _v0) * _fp64_g) + (((_v1.x * _v1.y) * 2.0) * _fp64_g)) + ((_v1.y * _v1.y) * _fp64_g));
return vec2(_v0, _v2);
}
vec2 df64_add(vec2 a, vec2 b, float _fp64_g) {
vec2 _v0 = df64_twoSum(a.x, b.x, _fp64_g);
vec2 _v1 = df64_twoSum(a.y, b.y, _fp64_g);
_v0.y = (_v0.y + _v1.x);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
_v0.y = (_v0.y + _v1.y);
_v0 = df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
return _v0;
}
vec2 df64_sub(vec2 a, vec2 b, float _fp64_g) {
return df64_add(a, (-b), _fp64_g);
}
vec2 df64_sqr(vec2 a, float _fp64_g) {
vec2 _v0 = df64_twoSqr(a.x, _fp64_g);
_v0.y = (_v0.y + ((a.x * a.y) * 2.0));
return df64_quickTwoSum(_v0.x, _v0.y, _fp64_g);
}
vec2 df64_sqrt(vec2 a, float _fp64_g) {
float _v0 = (_fp64_g / sqrt(a.x));
float _v1 = (a.x * _v0);
vec2 _v2 = (df64_twoSqr(_v1, _fp64_g) * _fp64_g);
float _v3 = df64_sub(a, _v2, _fp64_g).x;
vec2 _v4 = df64_twoProd((_v0 * 0.5), _v3, _fp64_g);
vec2 _v5 = df64_add(vec2(_v1, 0.0), _v4, _fp64_g);
return ((a.x == 0.0) ? vec2(0.0, 0.0) : _v5);
}
vec2 df64_floor(vec2 a, float _fp64_g) {
float _v0 = floor(a.x);
return ((_v0 == a.x) ? df64_quickTwoSum(_v0, floor(a.y), _fp64_g) : vec2(_v0, 0.0));
}
vec2 df64_fract(vec2 a, float _fp64_g) {
return df64_sub(a, df64_floor(a, _fp64_g), _fp64_g);
}
float df64_narrow(vec2 a) {
return (a.x + a.y);
}
in vec2 uv;
layout(location = 0) out vec4 _ret;
void main() {
float _fp64_g = texelFetch(_fp64, ivec2(0, 0), 0).x;
float _v0 = pow(10.0, (-u.zoom_exp));
float _v1 = (uv.x * 2.0);
bool _cse0 = (uv.x < 0.5);
float _v2 = (_v1 - (_cse0 ? 0.0 : 1.0));
float _v3 = ((_v2 - 0.5) * _v0);
float _v4 = (((uv.y - 0.5) * _v0) * ((u.resolution.y / u.resolution.x) * 2.0));
bool _v5 = (_cse0 || (u.fp64 < 0.5));
vec2 _cse1 = vec2(u.center.hi.x, u.center.lo.x);
vec2 _cse2 = vec2(u.center.hi.y, u.center.lo.y);
vec2 _lc0 = df64_add(_cse1, vec2(_v3, 0.0), _fp64_g);
vec2 _lc1 = df64_add(_cse2, vec2(_v4, 0.0), _fp64_g);
DF64Vec2 _v6 = DF64Vec2(vec2(_lc0.x, _lc1.x), vec2(_lc0.y, _lc1.y));
float _cse8 = uintBitsToFloat(floatBitsToUint(0.0));
vec2 _cse3 = vec2(_cse8, _cse8);
vec2 _cse4 = vec2(u.st_a.hi.x, u.st_a.lo.x);
vec2 _cse5 = vec2(u.st_a.hi.y, u.st_a.lo.y);
vec2 _gv0 = df64_add(vec2(_v6.hi.x, _v6.lo.x), _cse3, _fp64_g);
vec2 _gv1 = df64_add(vec2(_v6.hi.y, _v6.lo.y), _cse3, _fp64_g);
vec2 _v7 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse4, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse5, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
vec2 _cse6 = vec2(u.st_b.hi.x, u.st_b.lo.x);
vec2 _cse7 = vec2(u.st_b.hi.y, u.st_b.lo.y);
vec2 _v8 = df64_sqrt(df64_add(df64_sqr(df64_sub(_gv0, df64_add(_cse6, _cse3, _fp64_g), _fp64_g), _fp64_g), df64_sqr(df64_sub(_gv1, df64_add(_cse7, _cse3, _fp64_g), _fp64_g), _fp64_g), _fp64_g), _fp64_g);
float _v9 = df64_narrow(df64_fract((df64_sub(df64_add(_v7, _cse3, _fp64_g), df64_add(_v8, _cse3, _fp64_g), _fp64_g) * 0.25), _fp64_g));
float _v10 = df64_narrow(df64_fract((df64_add(_v7, _v8, _fp64_g) * 0.0625), _fp64_g));
vec2 _v11 = vec2((df64_narrow(_cse1) + _v3), (df64_narrow(_cse2) + _v4));
float _v12 = length((_v11 - vec2(df64_narrow(_cse4), df64_narrow(_cse5))));
float _v13 = length((_v11 - vec2(df64_narrow(_cse6), df64_narrow(_cse7))));
float _v14 = fract(((_v12 - _v13) * 0.25));
float _v15 = fract(((_v12 + _v13) * 0.0625));
float _v16 = (_v5 ? _v14 : _v9);
float _v17 = (_v5 ? _v15 : _v10);
float _v18 = min(_v16, (1.0 - _v16));
float _v19 = min(_v17, (1.0 - _v17));
float _v20 = ((fwidth(_v18) * 1.2) + 0.0001);
float _v21 = ((fwidth(_v19) * 1.2) + 0.0001);
float _v22 = (1.0 - smoothstep(0.0, _v20, _v18));
float _v23 = (1.0 - smoothstep(0.0, _v21, _v19));
vec3 _v24 = mix(vec3(0.02, 0.07, 0.13), vec3(0.04, 0.12, 0.2), uv.y);
_ret = vec4((((_v24 + (vec3(0.0, 0.06, 0.08) * _v16)) + (vec3(0.25, 0.95, 0.95) * (_v22 * 0.9))) + (vec3(0.95, 0.7, 0.25) * (_v23 * 0.35))), 1.0);
}

이 예제를 움직이는 컨트롤에 페이지가 넣을 값이 없어서, 이 페이지에는 그림이 없습니다.

WGSL과 GLSL 탭은 커밋 c66579bf의 컴파일러가 직접 낸 출력입니다. 컴파일러의 출력 검사가 구워 둔 골든 파일에서 그대로 읽어 왔습니다(emit-goldens.test.ts).

이 예제는 fn() 빌더 API로 작성해서 Playground의 편집기가 받지 않습니다.

GitHub의 파일

이 페이지 편집 문제 보고