--- id: game-shader-patterns title: Shader 패턴 — Vertex / Fragment / WGSL category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [game, shader, glsl, wgsl, vibe-coding] tech_stack: { language: "GLSL / WGSL", applicable_to: ["Game", "Frontend"] } applied_in: [] aliases: [shader, GLSL, WGSL, vertex shader, fragment shader, post-processing] --- # Shader Patterns > GPU 의 작은 program. **Vertex (위치 변형) + Fragment (색)**. WebGPU = WGSL, OpenGL/WebGL = GLSL. Effect / 사후 처리 강력. ## 📖 핵심 개념 - Vertex shader: 각 vertex 변형. - Fragment (pixel) shader: 각 pixel 색 결정. - Uniform: CPU → GPU 변수. - Attribute: vertex 별 data. ## 💻 코드 패턴 ### 단순 fragment (GLSL) ```glsl // fragment.glsl precision mediump float; varying vec2 vUv; uniform float uTime; void main() { vec3 color = vec3( sin(uTime + vUv.x * 6.28) * 0.5 + 0.5, sin(uTime + vUv.y * 6.28 + 2.09) * 0.5 + 0.5, sin(uTime + vUv.x * 6.28 + 4.18) * 0.5 + 0.5 ); gl_FragColor = vec4(color, 1.0); } ``` ### Three.js + GLSL ```ts const material = new THREE.ShaderMaterial({ vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` varying vec2 vUv; uniform float uTime; void main() { gl_FragColor = vec4(vUv, sin(uTime) * 0.5 + 0.5, 1.0); } `, uniforms: { uTime: { value: 0 } }, }); // 매 frame material.uniforms.uTime.value = clock.getElapsedTime(); ``` ### WGSL (WebGPU) ```wgsl struct VertexInput { @location(0) position: vec3f, @location(1) uv: vec2f, } struct VertexOutput { @builtin(position) position: vec4f, @location(0) uv: vec2f, } struct Uniforms { time: f32, } @group(0) @binding(0) var u: Uniforms; @vertex fn vs(input: VertexInput) -> VertexOutput { var out: VertexOutput; out.position = vec4f(input.position, 1.0); out.uv = input.uv; return out; } @fragment fn fs(input: VertexOutput) -> @location(0) vec4f { let color = vec3f( sin(u.time + input.uv.x * 6.28) * 0.5 + 0.5, sin(u.time + input.uv.y * 6.28) * 0.5 + 0.5, 1.0 ); return vec4f(color, 1.0); } ``` ### 자주 쓰는 함수 (GLSL/WGSL) ```glsl // Smoothstep — 부드러운 편향 float t = smoothstep(0.4, 0.6, dist); // Mix — 보간 vec3 c = mix(red, blue, t); // Distance float d = length(uv - vec2(0.5, 0.5)); // Step float v = step(0.5, x); // x < 0.5 ? 0 : 1 // Noise (snoise / perlin) // 라이브러리 또는 직접 구현 ``` ### Pattern: 원 ```glsl void main() { vec2 uv = vUv - 0.5; float d = length(uv); float circle = smoothstep(0.3, 0.29, d); gl_FragColor = vec4(vec3(circle), 1.0); } ``` ### Pattern: gradient ```glsl gl_FragColor = vec4(mix(vec3(1, 0, 0), vec3(0, 0, 1), vUv.y), 1.0); ``` ### Pattern: noise ```glsl // Simple hash noise float hash(vec2 p) { return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453); } float noise(vec2 p) { vec2 i = floor(p), f = fract(p); vec2 u = f * f * (3.0 - 2.0 * f); return mix( mix(hash(i), hash(i + vec2(1, 0)), u.x), mix(hash(i + vec2(0, 1)), hash(i + vec2(1, 1)), u.x), u.y ); } void main() { float n = noise(vUv * 10.0 + uTime); gl_FragColor = vec4(vec3(n), 1.0); } ``` ### Post-processing (Three.js) ```ts import { EffectComposer, RenderPass, ShaderPass } from 'three/examples/jsm/Addons.js'; const composer = new EffectComposer(renderer); composer.addPass(new RenderPass(scene, camera)); const myPass = new ShaderPass({ uniforms: { tDiffuse: { value: null }, uTime: { value: 0 } }, vertexShader: ..., fragmentShader: ` uniform sampler2D tDiffuse; uniform float uTime; varying vec2 vUv; void main() { vec4 color = texture2D(tDiffuse, vUv); // pixelate vec2 pixelated = floor(vUv * 100.0) / 100.0; color = texture2D(tDiffuse, pixelated); gl_FragColor = color; } `, }); composer.addPass(myPass); ``` ### Vertex displacement (wave) ```glsl // vertex uniform float uTime; varying vec2 vUv; void main() { vUv = uv; vec3 pos = position; pos.y += sin(pos.x * 5.0 + uTime) * 0.1; gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0); } ``` ### Texture ```glsl uniform sampler2D uTexture; varying vec2 vUv; void main() { vec4 color = texture2D(uTexture, vUv); // tint color.rgb *= vec3(1.0, 0.5, 0.8); gl_FragColor = color; } ``` ### React Three Fiber + custom material ```tsx import { extend } from '@react-three/fiber'; import { shaderMaterial } from '@react-three/drei'; const MyMaterial = shaderMaterial( { uTime: 0, uColor: new THREE.Color('hotpink') }, vertexShader, fragmentShader ); extend({ MyMaterial }); function Mesh() { const ref = useRef(); useFrame((_, dt) => { ref.current.uTime += dt; }); return ( ); } ``` ### Compute shader (WebGPU only) ```wgsl @group(0) @binding(0) var input: array; @group(0) @binding(1) var output: array; @compute @workgroup_size(64) fn main(@builtin(global_invocation_id) id: vec3u) { let i = id.x; output[i] = input[i] * 2.0; } ``` → Particle simulation / image processing. ### Performance ``` - Texture lookup 비싸 — minimize. - Branch (if) 가능한 한 적게 — uniform branch 는 OK, dynamic branch 비싼. - Precision: highp (vertex) / mediump (fragment) 보통 OK. - mipmaps 사용 (texture). - Dependent texture lookup 피하기. - Discard 비싸 — alpha test 알 됨. ``` ### 디버그 ``` Spector.js (browser extension): WebGL/WebGPU frame capture. Renderdoc: native graphics debugger. GLSL Lint (vscode): syntax 검사. Print debug: gl_FragColor = vec4(vec3(myValue), 1.0); // 색으로 값 시각화. ``` ### Shadertoy / The Book of Shaders - shadertoy.com — 영감. - thebookofshaders.com — 학습. - 공유된 shader 가 사실상 라이브러리. ### Common shader effects ``` - Glitch / chromatic aberration - Bloom - DOF (depth of field) - Outline - Toon / cel shading - Water / wave - Fire / smoke - Motion blur - Pixelation - Gaussian blur - Edge detection ``` ## 🤔 의사결정 기준 | 작업 | 도구 | |---|---| | 일반 web shader | Three.js + GLSL | | Modern + WebGPU | R3F + WGSL | | 2D effect | PixiJS filter | | Image processing | WebGPU compute | | Native game | Unity/Unreal shader graph | | Quick prototype | Shadertoy | ## ❌ 안티패턴 - **Branch heavy 모든 fragment**: 느림. lookup texture. - **Texture 매 frame upload**: cache. - **High precision 모든 곳**: mediump 충분. - **uniform 매 draw 변경**: dirty flag. - **Discard 큰 사용**: alpha test 또는 sort. - **Compile 매 frame**: cache. - **Shader 큰 size**: 중복 로직 — function 분리. ## 🤖 LLM 활용 힌트 - 공유 shader (Shadertoy) → 자체 변형. - WebGPU = WGSL (modern). - R3F + drei `shaderMaterial` 가 가장 단순. - Spector.js 가 디버그. ## 🔗 관련 문서 - [[Frontend_Three_R3F]] - [[Frontend_WebGPU_Patterns]] - [[Game_Loop_ECS]]