Gradient pass
섞는 비율을 조절할 수 있는 세로 방향 두 색 그러데이션.
// ═══ typeshade example — fullscreen gradient pass ═══//// A self-contained render pass: an oversized fullscreen triangle (no vertex buffer)// feeds a fragment stage that mixes two colours by the screen-space UV, modulated by a// uniform. Shows the std140 uniform block + the `If/elif` control-flow combinator. The// /shader-dsl site page packs `top`/`bottom`/`mix_bias` into the UBO from reflect().
import { fn, module, vec2, vec4, f32, mix, f32T, u32T, vec2fT, vec4fT, If, ioStruct, builtin, location, uniformStruct,} from '../src/index.js';import type { ShaderExample } from './_shared.js';
// Uniform block — std140-laid-out by reflect(). `top`/`bottom` are the two gradient// endpoints; `mix_bias` shifts the blend up or down.const U = uniformStruct( 'Uniforms', { group: 0, binding: 0, as: 'u' }, { top: vec4fT, bottom: vec4fT, mix_bias: f32T, },);
// Vertex → fragment interface struct.const VsOut = ioStruct('VsOut', { pos: builtin('position', vec4fT), uv: location(0, vec2fT),});
// Oversized fullscreen triangle (3 verts, NDC −1..3) — covers the screen from a single// non-indexed draw with no vertex buffer.const vsFull = fn( 'vs_full', { idx: builtin('vertex_index', u32T) }, (p) => { const pos = vec2(-1, -1); If(p.idx.eq(1), () => { pos.assign(vec2(3, -1)); }).elif(p.idx.eq(2), () => { pos.assign(vec2(-1, 3)); }); return VsOut.construct({ pos: vec4(pos, 0, 1), uv: vec2(pos.x.add(1).mul(0.5), pos.y.add(1).mul(0.5)), }); }, { stage: 'vertex' },);
// Fragment — vertical gradient between the two uniform colours, biased. `vo` (not `in`,// a GLSL reserved word) is the fragment input.const fsGradient = fn( 'fs_gradient', { vo: VsOut }, (p) => { const t = p.vo.uv.y.add(U.field.mix_bias); const rgb = mix(U.field.bottom.rgb, U.field.top.rgb, t); return vec4(rgb, f32(1)); }, { stage: 'fragment', retAttr: '@location(0)' },);
const gradientModule = module({ structs: [U.struct, VsOut.decl], bindings: [U.binding], funcs: [vsFull, fsGradient],});
export const gradient: ShaderExample = { id: 'gradient', title: 'Gradient pass', blurb: 'A two-colour vertical gradient with a biasable blend — the minimal render pass. `top`/`bottom`/`mix_bias` are packed into one std140 uniform block recovered from reflect().', category: 'generic', file: 'gradient-pass.ts', module: gradientModule, renderable: true, controls: { top: { kind: 'const', value: [0.16, 0.5, 0.95, 1] }, // sky blue bottom: { kind: 'const', value: [0.02, 0.03, 0.09, 1] }, // near-black mix_bias: { kind: 'slider', label: 'Mix bias', min: -0.5, max: 0.5, step: 0.01, value: 0 }, },};struct Uniforms { top: vec4<f32>, bottom: vec4<f32>, mix_bias: f32,}
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
@group(0) @binding(0) var<uniform> u: Uniforms;
@vertexfn vs_full(@builtin(vertex_index) idx: u32) -> VsOut { var _av0: vec2<f32> = vec2<f32>(-1.0, -1.0); if ((idx == 1u)) { _av0 = vec2<f32>(3.0, -1.0); } else if ((idx == 2u)) { _av0 = vec2<f32>(-1.0, 3.0); } return VsOut(vec4<f32>(_av0, 0.0, 1.0), vec2<f32>(((_av0.x + 1.0) * 0.5), ((_av0.y + 1.0) * 0.5)));}
@fragmentfn fs_gradient(vo: VsOut) -> @location(0) vec4<f32> { return vec4<f32>(mix(u.bottom.rgb, u.top.rgb, (vo.uv.y + u.mix_bias)), 1.0);}#version 300 esprecision highp float;precision highp int;
out vec2 uv;
void main() { uint idx = uint(gl_VertexID); vec2 _av0 = vec2(-1.0, -1.0); if ((idx == 1u)) { _av0 = vec2(3.0, -1.0); } else if ((idx == 2u)) { _av0 = vec2(-1.0, 3.0); } gl_Position = vec4(_av0, 0.0, 1.0); uv = vec2(((_av0.x + 1.0) * 0.5), ((_av0.y + 1.0) * 0.5));}#version 300 esprecision highp float;precision highp int;
layout(std140) uniform Uniforms { vec4 top; vec4 bottom; float mix_bias;} u;in vec2 uv;layout(location = 0) out vec4 _ret;
void main() { _ret = vec4(mix(u.bottom.rgb, u.top.rgb, (uv.y + u.mix_bias)), 1.0);}Gradient pass. 빌드할 때 그린 화면입니다.
WGSL과 GLSL 탭은 커밋 c66579bf의 컴파일러가 직접 낸 출력입니다. 컴파일러의 출력 검사가 구워 둔 골든 파일에서 그대로 읽어 왔습니다(emit-goldens.test.ts).
이 예제는 fn() 빌더 API로 작성해서 Playground의 편집기가 받지 않습니다.