GPU types: TypeScript 표면에서 GPU 값으로
TypeShade의 타입은 TypeScript 문법으로 작성하지만 TypeScript의 모든 런타임 값 타입을 그대로 가져오지는 않습니다. 이 페이지에서는 GPU에서 실제 계산되는 값의 종류를 중심으로 봅니다.
1. TypeScript 타입과 GPU 타입의 관계
TypeScript의 number처럼 넓은 런타임 타입을 그대로 shader 값으로 취급하기보다 f32, i32, u32처럼 GPU 표현을 명시합니다. vec*, mat*도 JavaScript 객체가 아니라 GPU arithmetic value입니다.
// TypeScript surface, GPU-specific value typeslet value: f32 = 1let index: u32 = 0let direction: vec3 = vec3(0, 1, 0)2. 스칼라
단일 숫자 값은 GPU scalar 타입으로 표현합니다. 리소스와 struct field의 타입을 명시할 때 사용합니다.
3. 벡터
vec2, vec3, vec4는 여러 scalar 값을 하나의 GPU 값으로 묶습니다. shader 계산과 vertex/fragment 데이터에서 자주 사용합니다.
const uv: vec2 = vec2(0.5, 0.5)const color: vec4 = vec4(1, 0, 0, 1)4. 행렬
mat4 같은 matrix 타입은 변환 계산에 사용합니다. TypeScript 객체가 아니라 GPU arithmetic value입니다.
type Camera = { view: mat4}5. 배열
array는 여러 GPU 값을 하나의 타입으로 표현합니다. resource element type과 함께 사용하면 host가 제공하는 buffer shape를 명확하게 설명할 수 있습니다.
declare const pixels: storage<array<f32>, "read_write">6. 라이브 예제
아래 진입점은 줄마다 GPU 타입이 하나씩 나옵니다. 위치는 vec2, 거리와 반지름은 f32, 색은 vec3입니다.
"use typeshade"
class Uniforms { resolution: vec2 center: vec2 radius: f32 tint: vec3}
declare const u: uniform<Uniforms>
@fragmentexport function main(@location(0) uv: vec2): vec4 { const ratio: f32 = u.resolution.x / u.resolution.y const p: vec2 = (uv - u.center) * vec2(ratio, 1.) const d: f32 = length(p) - u.radius const edge: f32 = 1. - smoothstep(0., 0.008, d) return vec4(mix(vec3(0.15, 0.14, 0.91), u.tint, edge), 1.)}
d는 픽셀에서 원반 테두리까지의 거리입니다. 안쪽은 음수, 테두리 위는 0, 바깥쪽은 양수입니다. edge는 그 f32 하나를 마스크로 바꾸고, mix는 그 마스크를 읽어 색을 고릅니다.
페이지가 매 프레임 채우는 값이라 컨트롤이 없습니다: resolution.
컨트롤을 움직이거나 한 줄을 고쳐서 다음을 해 보십시오:
p줄의vec2(ratio, 1.)을vec2(1., 1.)로 바꿔 보십시오. 원반이 늘어나 타원이 됩니다.ratio가 x 성분에만 곱해지고 있었고, 성분마다 다르게 다루는 것이vec2로 할 수 있는 일입니다.return줄을return vec4(vec3(fract(d * 10.)), 1.)로 바꿔 보십시오. 나타나는 고리가 바로d입니다.vec3은 그f32하나를 색 채널 세 개에 그대로 펼칩니다.length(p)를max(abs(p.x), abs(p.y))로 바꾸면 원반이 정사각형이 되고,abs(p.x) + abs(p.y)로 바꾸면 마름모가 됩니다. 원하는 모양을 재는f32를 직접 써 보십시오.
출력 결과
컴파일러가 본 모듈 전체입니다. 버텍스 진입점과 그것이 돌려주는 VsOut 구조체는 페이지가 붙였고, 나머지는 위 파일입니다.
struct VsOut { @builtin(position) pos: vec4<f32>, @location(0) uv: vec2<f32>,}
struct Uniforms { resolution: vec2<f32>, center: vec2<f32>, radius: f32, tint: vec3<f32>,}
@group(0) @binding(0) var<uniform> u: Uniforms;
@vertexfn fullscreen(@builtin(vertex_index) i: u32) -> VsOut { let x = select(-1.0, 3.0, (i == 1u)); let y = select(-1.0, 3.0, (i == 2u)); return VsOut(vec4<f32>(x, y, 0.0, 1.0), vec2<f32>(((x * 0.5) + 0.5), ((y * 0.5) + 0.5)));}
@fragmentfn main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> { let ratio = (u.resolution.x / u.resolution.y); let p = ((uv - u.center) * vec2<f32>(ratio, 1.0)); let d = (length(p) - u.radius); let edge = (1.0 - smoothstep(0.0, 0.008, d)); return vec4<f32>(mix(vec3<f32>(0.15, 0.14, 0.91), u.tint, edge), 1.0);}#version 300 esprecision highp float;precision highp int;
out vec2 uv;
void main() { uint i = uint(gl_VertexID); float x = ((i == 1u) ? 3.0 : -1.0); float y = ((i == 2u) ? 3.0 : -1.0); gl_Position = vec4(x, y, 0.0, 1.0); uv = vec2(((x * 0.5) + 0.5), ((y * 0.5) + 0.5));}#version 300 esprecision highp float;precision highp int;
layout(std140) uniform Uniforms { vec2 resolution; vec2 center; float radius; vec3 tint;} u;in vec2 uv;layout(location = 0) out vec4 _ret;
void main() { float ratio = (u.resolution.x / u.resolution.y); vec2 p = ((uv - u.center) * vec2(ratio, 1.0)); float d = (length(p) - u.radius); float edge = (1.0 - smoothstep(0.0, 0.008, d)); _ret = vec4(mix(vec3(0.15, 0.14, 0.91), u.tint, edge), 1.0);}| 필드 | 타입 | 오프셋 |
|---|---|---|
resolution | vec2<f32> | 0 |
center | vec2<f32> | 8 |
radius | f32 | 16 |
tint | vec3<f32> | 32 |