Gradient pass

Gradient pass

섞는 비율을 조절할 수 있는 세로 방향 두 색 그러데이션.

TypeScript로 쓰고 TypeShade가 컴파일한 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;
@vertex
fn 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)));
}
@fragment
fn 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 es
precision 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 es
precision 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의 편집기가 받지 않습니다.

GitHub의 파일

이 페이지 편집 문제 보고