GPU 타입
이 페이지에서

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 types
let value: f32 = 1
let index: u32 = 0
let 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입니다.

disc.shade.ts
"use typeshade"
class Uniforms {
resolution: vec2
center: vec2
radius: f32
tint: vec3
}
declare const u: uniform<Uniforms>
@fragment
export 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.)
}
Playground에서 열기
TypeScript로 쓰고 TypeShade가 컴파일한 원반 셰이더. 빌드할 때 렌더링함.
원반 하나입니다. vec2가 자리를, f32가 크기를, vec3이 색을 정합니다.

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 구조체는 페이지가 붙였고, 나머지는 위 파일입니다.

WGSL
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;
@vertex
fn 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)));
}
@fragment
fn 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);
}
GLSL ES 3.00 버텍스
#version 300 es
precision 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));
}
GLSL ES 3.00 프래그먼트
#version 300 es
precision 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);
}
유니폼 블록
필드타입오프셋
resolutionvec2<f32>0
centervec2<f32>8
radiusf3216
tintvec3<f32>32

다음: Resources

이 페이지 편집 문제 보고