2D array texture
타일 아틀라스를 texture_2d_array<f32> 바인딩 하나로 씁니다.
// ═══ typeshade example — 2d-array texture, sampled per LAYER (#1651) ═══//// A tile/glyph ATLAS as one `texture_2d_array<f32>` binding: N layers behind a single// binding slot, with the layer chosen per SAMPLE instead of per bind group. The three// array reads the DSL exposes all appear here://// • textureSample(tex, smp, uv, layer) — filtered, implicit LOD// (FRAGMENT-only, like its 2d twin)// • textureSampleLevel(tex, smp, uv, layer, level) — filtered, EXPLICIT LOD (any stage)// • textureLoad(tex, coord, layer, level) — unfiltered texel fetch//// Each carries its own NEUTRAL intrinsic id, because the two targets restructure the// arguments rather than merely adding one: WGSL appends the layer// (`textureSample(t, s, uv, layer)`), GLSL ES 3.00 folds it into the coordinate// (`texture(sampler2DArray, vec3(uv, float(layer)))`).//// NOT WebGL2-renderable (`renderable: false`) — not a backend limit (2d-array is CORE// GLSL ES 3.00, sampler2DArray, and needs no Capability), but a HARNESS one: the site's// live runner binds one UBO and the fp64 guard texture, and has no way to supply an// atlas. The site shows its WGSL + reflection (whose texture entry now names// `textureDim: '2d-array'`); the real-WebGL2 compile + per-layer readback lives in// playground/e2e/_glsl-texture-array-gate.spec.ts.
import { fn, module, vec2, vec2i, vec4, f32, u32, mix, u32T, vec2fT, vec4fT, If, ioStruct, builtin, location, resource, samplerT, texture2dArrayfT, textureSample, textureSampleLevel, textureLoad,} from '../src/index.js';import type { ShaderExample } from './_shared.js';
// One binding for the whole atlas — the layer is an argument, not a bind-group switch.const atlas = resource('atlas', texture2dArrayfT, { group: 0, binding: 0 });const atlasSampler = resource('atlas_sampler', samplerT, { group: 0, binding: 1 });
// Which layer holds what. Plain numbers: a `number` layer lifts to an i32 literal// (WGSL's array_index is i32/u32 — an f32 `0.0` there is a type error).const LAYER_BASE = 0;const LAYER_DETAIL = 1;
const VsOut = ioStruct('VsOut', { pos: builtin('position', vec4fT), uv: location(0, vec2fT),});
// Oversized fullscreen triangle (3 verts, NDC −1..3) — 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 — cross-fade the base layer (implicit LOD) into a coarse mip of the detail// layer (explicit LOD), tinted by an unfiltered corner texel of the base layer.const fsAtlas = fn( 'fs_atlas', { vo: VsOut }, (p) => { const base = textureSample(atlas.node, atlasSampler.node, p.vo.uv, LAYER_BASE); const detail = textureSampleLevel(atlas.node, atlasSampler.node, p.vo.uv, LAYER_DETAIL, 2); const corner = textureLoad(atlas.node, vec2i(0, 0), LAYER_BASE, u32(0)); return vec4(mix(base.rgb, detail.rgb, p.vo.uv.x).mul(corner.a), f32(1)); }, { stage: 'fragment', retAttr: '@location(0)' },);
const textureArrayModule = module({ structs: [VsOut.decl], bindings: [atlas.binding, atlasSampler.binding], funcs: [vsFull, fsAtlas],});
export const textureArrayLod: ShaderExample = { id: 'texture-array-lod', title: '2D array texture', blurb: 'A tile atlas as ONE `texture_2d_array<f32>` binding — the layer is a per-sample argument, not a bind-group switch. Shows all three array reads (implicit-LOD sample, explicit-LOD sample, unfiltered load); WGSL appends the layer, GLSL folds it into a `vec3` coordinate on a `sampler2DArray`.', category: 'generic', file: 'texture-array-lod.ts', module: textureArrayModule, renderable: false,};struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
@group(0) @binding(0) var atlas: texture_2d_array<f32>;@group(0) @binding(1) var atlas_sampler: sampler;
@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_atlas(vo: VsOut) -> @location(0) vec4<f32> { return vec4<f32>((mix(textureSample(atlas, atlas_sampler, vo.uv, 0).rgb, textureSampleLevel(atlas, atlas_sampler, vo.uv, 1, 2.0).rgb, vo.uv.x) * textureLoad(atlas, vec2<i32>(0, 0), 0, 0u).w), 1.0);}이 예제는 GLSL ES 3.00 형태가 없고 캔버스는 두 백엔드에서 같은 프로그램을 돌리므로, 이 페이지에는 그림이 없습니다.
WGSL과 GLSL 탭은 커밋 c66579bf의 컴파일러가 직접 낸 출력입니다. 컴파일러의 출력 검사가 구워 둔 골든 파일에서 그대로 읽어 왔습니다(emit-goldens.test.ts).
이 예제는 fn() 빌더 API로 작성해서 Playground의 편집기가 받지 않습니다.