쉐이더(Shader) 코드를 외부 자원으로 분리하기

Shader 코드를 외부 자원으로 분리해서 좀 더 깔끔하게 코드를 작성하고자 합니다. 물론 이런 분리는 유지보수 및 Shader에 대한 분리를 통해 유연성을 증가시키는 장점도 있습니다. Shader 코드를 shader.glsl이라는 파일로 작성했다고 할 때, 이 파일 자원을 불러와 사용하는 코드의 예시는 다음과 같습니다.

fetch("shader.glsl").then(response => {
    return response.text();
}).then(text => {
    const fragmentShaerCode = text;
    const material = new THREE.ShaderMaterial({

        ...

        vertexShader: `
            uniform float iTime;    
        
            varying vec2 vUv;

            void main() {
                vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix  * vec4(position,1.0);
            }
        `,
        
        fragmentShader: fragmentShaerCode
    });

    ....

}).catch(function (error) {
    console.warn(error);
});

vertex Shader는 분리되어 있지 않았지만 fragment Shader는 분리된 파일 자원(여기서는 shader.glsl)을 불러와 사용하고 있습니다.

GLSL, 원(Circle)

완전한 3차원 장면은 Shader로 완성된다고 할 수 있는데요. Shader를 통해 매우 사실적인 물, 안개, 번개 등과 같은 표현이 가능합니다. 아래는 Shader 언어 중 GLSL로 작성된 원을 렌더링하는 코드입니다.

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;

float circle(vec2 position, float radius) {
    return step(length(position), radius);
}

void main() {
    vec2 position = gl_FragCoord.xy / u_resolution;
    position -= 0.5;
    vec3 color = vec3(circle(position, 0.3));
    gl_FragColor = vec4(color, 1.0);

}

결과는 아래와 같구요.

코드를 보면 12번은 색상을 결정해야 하는 위치 좌표를 (0,0) ~(1,1)의 범위로 정규화해 줍니다. (0,0)은 화면의 좌측하단입니다. 13번은 다시 이 좌표를 (-0.5,-0.5) ~ (0.5,0.5)로 변경해 줍니다. 원의 가운데 위치하도록 하기 위함입니다. 8번은 원을 표현하기 위한 픽셀의 색상값을 결정하는 함수의 코드인데요. step(A, B)는 B가 A보다 크거나 같으면 1을 반환하고, 아니라면 0을 반환합니다.