정점 셰이더와 프래그먼트 셰이더라는 두 개의 강력한 도구를 손에 넣었지만, 그것을 휘두를 언어가 필요했다. 어떤 문법과 규칙을 사용해서 셰이더 프로그램을 작성할 것인가? 이 질문에 대한 답은 사실상 정해져 있었다.
GLSL (OpenGL Shading Language).
이름 그대로, OpenGL 생태계를 위해 태어난 셰이더 작성 전용 언어였다. C언어와 매우 유사한 문법을 가지고 있어 기존 개발자들이 배우기 쉬웠고, 무엇보다 벡터(Vector)와 행렬(Matrix) 연산에 특화된 자료형과 내장 함수들을 풍부하게 제공했다.
예를 들어, 두 벡터의 내적(Dot Product)을 구하는 것은 그래픽스에서 매우 흔한 연산이다. 일반적인 프로그래밍 언어에서는 루프를 돌며 각 요소의 곱을 더해야 하지만, GLSL에서는 dot(vectorA, vectorB)
라는 한 줄의 코드로 끝낼 수 있었다.
WebGL의 기반을 OpenGL ES 2.0으로 삼기로 결정한 순간부터, 셰이더 언어로 GLSL을 채택하는 것은 당연한 수순처럼 보였다.
하지만 문제는 그렇게 간단하지 않았다.
“우리가 사용할 GLSL의 버전을 명확히 해야 합니다.”
애플의 엔지니어가 문제를 제기했다. “OpenGL ES 2.0은 GLSL 버전 1.00을 사용합니다. 데스크톱 OpenGL의 GLSL과는 미묘하지만 중요한 차이점들이 있습니다. 우리는 이 사양을 엄격하게 따라야 합니다.”
그의 지적은 중요했다. 데스크톱 GLSL에는 더 많은 기능과 유연성이 있었지만, 그만큼 더 많은 파편화의 위험을 안고 있었다. 워킹 그룹은 WebGL이 모든 플랫폼에서 동일하게 동작해야 한다는 대원칙을 지키기 위해, 기능이 조금 더 제한적이더라도 OpenGL ES Shading Language 버전 1.00의 명세를 정확히 따르기로 합의했다.
이것이 첫 번째 관문이었다. 더 큰 문제는 다음에 기다리고 있었다.
“개발자가 작성한 이 GLSL 코드를 브라우저는 어떻게 처리해야 합니까?”
구글 크롬팀의 엔지니어가 근본적인 질문을 던졌다.
자바스크립트 개발자는 셰이더 코드를 보통 문자열(string) 형태로 작성하여 WebGL API에 전달하게 될 터였다.
const vertexShaderSource = `
attribute vec3 a_position;
// ...
`;
const vs = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vs, vertexShaderSource);
gl.compileShader(vs);
브라우저는 이 문자열을 받아서, 유효한 GLSL 코드인지 문법을 분석(Parsing)하고, 그래픽 드라이버가 이해할 수 있는 형태로 컴파일(Compile)해야 했다.
“만약 개발자가 셰이더 코드에 오타를 냈거나 문법 오류가 있다면 어떻게 하죠?”
“브라우저는 컴파일에 실패하고, 개발자에게 어떤 줄에서 어떤 오류가 났는지 알려줘야 합니다. gl.getShaderInfoLog()
같은 함수를 통해 오류 로그를 제공해야 합니다.”
여기까지는 순조로웠다. 진짜 논쟁은 그 다음 단계에서 터져 나왔다.
“보안 문제입니다.”
늘 그렇듯, 애플의 보안 아키텍트가 다시 나섰다. “우리가 그냥 GLSL 코드를 그대로 드라이버에 넘겨도 안전하다고 보장할 수 있습니까? 교묘하게 작성된 셰이더 코드가 드라이버의 컴파일러 버그를 유발하여 시스템을 위험에 빠뜨릴 가능성은 없습니까?”
그의 우려는 타당했다. 드라이버의 셰이더 컴파일러는 매우 복잡하고, 알려지지 않은 취약점이 존재할 수 있었다.
이 문제를 해결하기 위해, 구글과 모질라를 중심으로 한 진영에서 대담한 아이디어를 제안했다.
“브라우저가 셰이더 컴파일러가 되자.”
즉, 개발자가 작성한 GLSL 코드를 브라우저가 직접 검증하고 분석하는 것을 넘어, 아예 해당 코드를 더 안전하고 예측 가능한 다른 형태의 코드로 ‘변환(Translate)’한 뒤에 드라이버에 전달하자는 것이었다.
예를 들어, 브라우저는 개발자의 GLSL 코드를 받아서 루프가 무한정 돌지 않는지, 허용되지 않은 메모리에 접근하지 않는지 등을 정적으로 분석한다. 그리고 이 분석을 통과한 안전한 코드만을, 드라이버가 가장 잘 이해할 수 있는 형태의 기본적인 GLSL 코드로 다시 써서(Rewrite) 전달하는 것이다.
이 프로젝트는 ANGLE(Almost Native Graphics Layer Engine) 이라는 이름으로 구글에 의해 주도되고 있었고, 마이크로소프트의 DirectX와 OpenGL 사이의 변환을 목표로 하고 있었다. WebGL 워킹 그룹은 이와 유사한 검증 및 변환 계층(Validation and Translation Layer)을 표준의 일부로 간주하기 시작했다.
이것은 셰이더 보안에 대한 획기적인 접근법이었다. 드라이버의 컴파일러를 완전히 믿지 않고, 그 앞에 브라우저라는 강력한 ‘사전 검열관’을 두는 셈이었다.
GLSL을 셰이더 언어로 채택하기로 한 결정은, 단순히 언어 하나를 선택한 것이 아니었다. 그것은 브라우저가 어떻게 셰이더 코드를 안전하게 처리하고 컴파일하며, 웹 개발자에게 어떤 방식으로 오류를 보고할지에 대한 복잡한 메커니즘 전체를 설계하는 과정이었다. WebGL의 심장인 셰이더를 움직일 피와 혈관이 마침내 정의되고 있었다.