소개
원그리기
원의 공식은 입니다. 이를 셰이더 코드로 변환하면 다음과 같습니다.
임으로 각 픽셀의 색상을 정하는 fragment shader에 전달하는 x, y 좌표값을 사용하여 다음과 같이 셰이더 코드를 작성할 수 있습니다.
const fragmentNode = React.useMemo(() => {
const p = positionLocal;
const radius = 0.5;
const circle = p.x
.mul(p.x)
.add(p.y.mul(p.y))
.sub(radius * radius)
.abs()
.step(0.01);
const line = circle;
return line;
}, []);
즉, x, y 좌표가 원의 경계에 있을 때 0에 가까운 값이 되고, 원 내부에서는 음수, 원 외부에서는 양수가 됩니다.
절대 값을 취한 뒤, 특정 임계값(예: 0.01)을 기준으로 계단 함수를 적용하여 원의 경계 근처에서만 1에 가까운 값을 갖도록 합니다. 이를 통해 원의 테두리를 그릴 수 있습니다.
Vertex Shader WGSL 생성 코드
Fragment Shader WGSL생성 코드
함수 화
Circle 함수를 정의하여 재사용할 수 있습니다.
const Circle = Fn(([p, r, t]: [p: typeof positionLocal, radius: number, thickness: number]) => {
const distance = length(p).sub(r);
return smoothstep(t, 0.0, abs(distance));
});
const fragmentNode = React.useMemo(() => {
const p = positionLocal;
const radius = 0.1;
const thickness = 0.01;
const circleOffset = vec2(-0.33, 0.33);
const circle = Circle(p.sub(circleOffset), radius, thickness);
return circle;
}, []);
circleOffset 값을 변경하여 원의 위치를 조정할 수 있습니다.
Vertex Shader WGSL 생성 코드
Fragment Shader WGSL생성 코드
원 안의 회전하는 선
먼저 특정 원 안에 선을 그리는 Line 함수를 정의합니다.
const Line = Fn(
([p, dir, distance, t]: [p: typeof positionLocal, direction: JoinNode, distance: number, thickness: number]) => {
const projected = p.dot(dir);
const clampledProjection = projected.clamp(0.0, distance);
return smoothstep(t, 0.0, length(p.sub(clampledProjection.mul(dir))));
}
);
direction은 선의 방향을 말합니다. 임의의 점과 내적을하면 원 내부는 음수, 원 외부는 양수가 됩니다. 이 값을 clamp 함수를 사용하여 0과 distance 사이로 제한합니다.
그런 다음, 이 값을 다시 방향 벡터와 곱하여 원 내부의 점으로 투영합니다. 마지막으로, 원 내부의 점과 원의 중심에서 투영된 점 사이의 거리를 계산하여 선의 두께에 따라 계단 함수를 적용합니다.
이제 원과 선을 결합하여 원 내부에서 회전하는 선을 그릴 수 있습니다.
return mix(circle, vec3(1, 0, 0), radiusLine);
Vertex Shader WGSL 생성 코드
Fragment Shader WGSL생성 코드
마치며
최종 결과물은 다음과 같습니다.
Vertex Shader WGSL 생성 코드
Fragment Shader WGSL생성 코드
tsl를 사용하여 원, 선, 곡선 등을 그려보았습니다.
mix 함수와 smoothstep 함수를 적절히 활용하면 다양한 도형과 패턴을 셰이더로 구현할 수 있습니다.