Tunnel

Tunnel

데모신 시절의 터널.

TypeScript로 쓰고 TypeShade가 컴파일한 Tunnel 셰이더. 빌드할 때 렌더링함.
// ═══ typeshade example — the classic tunnel (polar remap) ═══
//
// The demoscene / ShaderToy staple: remap the screen into polar coordinates
// (angle around the bore, 1/radius into the depth) so a flat checker pattern
// reads as an endless tunnel flying past. Showcases `atan2` (the divergent
// WGSL/GLSL spelling the intrinsic registry unifies), `length`, and
// screen-space fog. One DSL source → WGSL (WebGPU) + GLSL ES 3.00 (WebGL2).
import {
fn,
module,
f32,
vec3,
vec4,
sin,
atan2,
length,
max,
mix,
clamp,
smoothstep,
Let,
f32T,
} from '../src/index.js';
import { VsOut, vs, fullscreenUniforms, screenCoords } from './_fullscreen.js';
import type { ShaderExample } from './_shared.js';
const U = fullscreenUniforms({ twist: f32T });
const fs = fn(
'fs',
{ vo: VsOut },
({ vo }) => {
const t = U.field.time;
const res = U.field.resolution;
// centred, aspect-corrected plane
const p = screenCoords(vo.uv, res);
const r = Let(length(p));
const a = atan2(p.y, p.x);
// tunnel-surface coordinates: 1/r is the distance into the bore, the
// angle wraps around it. Adding time to the depth flies the camera forward;
// the twist uniform shears the angle by depth so the bore corkscrews.
const depth = Let(f32(0.3).div(max(r, 0.001)).add(t.mul(1.4)));
const ang = a.div(3.14159265).add(depth.mul(U.field.twist).mul(0.08));
// checkerboard walls: two perpendicular square-ish waves
const cw = sin(ang.mul(12.566)).mul(sin(depth.mul(9.4248)));
const shade = smoothstep(-0.6, 0.6, cw).mul(0.55).add(0.35);
// warm→cool tint cycling with depth, then fog toward the far centre
const tint = mix(
vec3(1.0, 0.62, 0.28),
vec3(0.42, 0.3, 0.55),
sin(depth.mul(0.9)).mul(0.5).add(0.5),
);
const fog = smoothstep(0.0, 0.55, r);
const vig = clamp(f32(1.15).sub(r.mul(0.35)), 0, 1);
return vec4(tint.mul(shade).mul(fog).mul(vig), 1);
},
{ stage: 'fragment', retAttr: '@location(0)' },
);
const tunnelModule = module({
structs: [U.struct, VsOut.decl],
bindings: [U.binding],
funcs: [vs, fs],
});
export const tunnel: ShaderExample = {
id: 'tunnel',
title: 'Tunnel',
blurb:
'The demoscene tunnel — the screen remapped to polar coordinates (angle around, 1/radius into the depth) so a checker pattern flies past forever. atan2 + length + fog, with a live twist control.',
category: 'generic',
file: 'tunnel.ts',
module: tunnelModule,
renderable: true,
controls: {
time: { kind: 'time' },
resolution: { kind: 'resolution' },
twist: { kind: 'slider', label: 'Twist', min: 0, max: 3, step: 0.1, value: 1 },
},
};
struct Uniforms {
time: f32,
resolution: vec2<f32>,
twist: f32,
}
struct VsOut {
@builtin(position) pos: vec4<f32>,
@location(0) uv: vec2<f32>,
}
@group(0) @binding(0) var<uniform> U: Uniforms;
@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(vo: VsOut) -> @location(0) vec4<f32> {
let _cse0 = vec2<f32>((((vo.uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((vo.uv.y * 2.0) - 1.0));
let _v0 = length(_cse0);
let _v1 = ((0.3 / max(_v0, 0.001)) + (U.time * 1.4));
return vec4<f32>((((mix(vec3<f32>(1.0, 0.62, 0.28), vec3<f32>(0.42, 0.3, 0.55), ((sin((_v1 * 0.9)) * 0.5) + 0.5)) * ((smoothstep(-0.6, 0.6, (sin((((atan2(_cse0.y, _cse0.x) / 3.14159265) + ((_v1 * U.twist) * 0.08)) * 12.566)) * sin((_v1 * 9.4248)))) * 0.55) + 0.35)) * smoothstep(0.0, 0.55, _v0)) * clamp((1.15 - (_v0 * 0.35)), 0.0, 1.0)), 1.0);
}
#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;
layout(std140) uniform Uniforms {
float time;
vec2 resolution;
float twist;
} U;
in vec2 uv;
layout(location = 0) out vec4 _ret;
void main() {
vec2 _cse0 = vec2((((uv.x * 2.0) - 1.0) * (U.resolution.x / U.resolution.y)), ((uv.y * 2.0) - 1.0));
float _v0 = length(_cse0);
float _v1 = ((0.3 / max(_v0, 0.001)) + (U.time * 1.4));
_ret = vec4((((mix(vec3(1.0, 0.62, 0.28), vec3(0.42, 0.3, 0.55), ((sin((_v1 * 0.9)) * 0.5) + 0.5)) * ((smoothstep(-0.6, 0.6, (sin((((atan(_cse0.y, _cse0.x) / 3.14159265) + ((_v1 * U.twist) * 0.08)) * 12.566)) * sin((_v1 * 9.4248)))) * 0.55) + 0.35)) * smoothstep(0.0, 0.55, _v0)) * clamp((1.15 - (_v0 * 0.35)), 0.0, 1.0)), 1.0);
}

Tunnel. 빌드할 때 그린 화면입니다.

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

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

GitHub의 파일

이 페이지 편집 문제 보고