이건 WebGL 기초에서 이어지는 글입니다. 대부분의 WebGL 강의들은 모든걸 한 번에 다루기 때문에 배우기 복잡해 보이기도 하는데요. 가능한 한 그것을 피하기 위해 더 작게 나눠보려고 합니다.
WebGL을 복잡해 보이도록 만드는 것들 중 하나는 정점 셰이더와 프래그먼트 셰이더, 두 가지 작은 함수가 있다는 겁니다. 이 두 함수는 일반적으로 최대 속도가 나오는 GPU에서 실행되는데요. 그렇기 때문에 GPU에서 수행할 수 있는 언어, 커스텀 언어로 작성됩니다. 이 두 함수는 컴파일되고 연결되어야 하는데요. 해당 처리는 모든 WebGL 프로그램에서 99% 동일합니다.
다음은 셰이더를 컴파일 하는 상용구 코드입니다.
/**
* 셰이더 생성 및 컴파일
*
* @param {!WebGLRenderingContext} gl은 WebGL Context
* @param {string} shaderSource는 셰이더의 GLSL 소스 코드
* @param {number} shaderType은 셰이더의 타입, VERTEX_SHADER 또는 FRAGMENT_SHADER
* @return {!WebGLShader} 셰이더
*/
function compileShader(gl, shaderSource, shaderType) {
// 셰이더 객체 생성
var shader = gl.createShader(shaderType);
// 셰이더 소스 코드 설정
gl.shaderSource(shader, shaderSource);
// 셰이더 컴파일
gl.compileShader(shader);
// 컴파일 여부 확인
var success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!success) {
// 컴파일하는 동안 문제 발생
throw "could not compile shader:" + gl.getShaderInfoLog(shader);
}
return shader;
}
그리고 프로그램에 두 셰이더를 연결하는 상용구 코드입니다.
/**
* 두 셰이더로 프로그램 생성합니다.
*
* @param {!WebGLRenderingContext) gl은 WebGL Context
* @param {!WebGLShader} vertexShader는 정점 셰이더
* @param {!WebGLShader} fragmentShader는 프래그먼트 셰이더
* @return {!WebGLProgram} 프로그램
*/
function createProgram(gl, vertexShader, fragmentShader) {
// 프로그램 생성
var program = gl.createProgram();
// 셰이더 할당
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
// 프로그램 연결
gl.linkProgram(program);
// 연결 여부 확인
var success = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!success) {
// 연결에 문제 발생
throw ("program failed to link:" + gl.getProgramInfoLog (program));
}
return program;
};
물론 오류를 처리하는 방법은 다를 수 있습니다. 예외를 던지는 것이 오류를 처리하는 최고의 방법은 아니죠. 그럼에도 불구하고 거의 모든 WebGL 프로그램에서 이 코드는 거의 비슷합니다.
저는 셰이더를 자바스크립트가 아닌 <script> tag에 저장하는 걸 좋아하는데요. 이 방식은 코드를 수정하기 쉽게 만들주기 때문에 저는 이렇게 코드를 작성합니다.
/**
* 스크립트 태그의 내용으로 셰이더 생성
*
* @param {!WebGLRenderingContext) gl은 WebGL Context
* @param {string} scriptId는 스크립트 태그의 id
* @param {string} opt_shaderType는 생성할 셰이더의 타입
* 전달되지 않으면 스크립트 태그의 타입 속성 사용
* @return {!WebGLShader} 셰이더
*/
function createShaderFromScript(gl, scriptId, opt_shaderType) {
// id로 스크립트 태그 탐색
var shaderScript = document.getElementById(scriptId);
if (!shaderScript) {
throw("*** Error: unknown script element" + scriptId);
}
// 스크립트 태그의 내용 추출
var shaderSource = shaderScript.text;
// 타입을 넘기지 않으면, 스크립트 태그의 'type' 사용
if (!opt_shaderType) {
if (shaderScript.type == "x-shader/x-vertex") {
opt_shaderType = gl.VERTEX_SHADER;
} else if (shaderScript.type == "x-shader/x-fragment") {
opt_shaderType = gl.FRAGMENT_SHADER;
} else if (!opt_shaderType) {
throw("*** Error: shader type not set");
}
}
return compileShader(gl, shaderSource, opt_shaderType);
};
이제 셰이더를 컴파일할 수 있습니다.
var shader = compileShaderFromScript(gl, "someScriptTagId");
한 걸음 더 나아가서 스크립트 태그에서 두 셰이더를 컴파일하고, 프로그램에 첨부한 다음 연결하는 함수를 만들겁니다.
/**
* 두 스크립트 태그에서 프로그램 생성
*
* @param {!WebGLRenderingContext} gl은 WebGL Context
* @param {string[]} shaderScriptId는 셰이더용 스크립트 태그의 id 배열입니다.
* 첫 번째는 정점 셰이더, 두 번째는 프래그먼트 셰이더라고 가정합니다.
* @return {!WebGLProgram} 프로그램
*/
function createProgramFromScripts(gl, shaderScriptIds) {
var vertexShader = createShaderFromScript(gl, shaderScriptIds[0], gl.VERTEX_SHADER);
var fragmentShader = createShaderFromScript(gl, shaderScriptIds[1], gl.FRAGMENT_SHADER);
return createProgram(gl, vertexShader, fragmentShader);
}
거의 모든 WebGL 프로그램에서 사용하는 다른 코드는 캔버스의 크기를 조정하는 코드입니다. 여기에서 해당 함수가 어떻게 구현됐는지 볼 수 있습니다.
모든 예제는 이 두 가지 함수를 있고,
<script src="resources/webgl-utils.js"></script>
그리고 이렇게 사용하는데,
var program = webglUtils.createProgramFromScripts(gl, [idOfVertexShaderScript, idOfFragmentShaderScript]);
...
webglUtils.resizeCanvasToMatchDisplaySize(canvas);
특정한 예제의 목적을 방해하기 때문에 여러 줄의 동일한 코드로 어지럽히지 않는 게 가장 좋습니다.
여기까지가 최소한의 WebGL 상용구 코드 모음입니다.
webgl-utils.js
코드는 여기에서 찾으실 수 있습니다.
좀 더 정리된 것을 원하신다면 TWGL.js를 확인해주세요.
WebGL을 복잡하게 보이게 만드는 나머지 부분은 셰이더에 모든 입력을 설정하는 겁니다. 이건 작동 원리를 봐주세요.
또한 유틸리티 함수를 읽고 TWGL를 확인하는 걸 추천합니다.
참고로 비슷한 이유로 추가하는 몇 가지 스크립트가 더 있습니다.
이건 슬라이더를 드래그할 때 업데이트되는 표시 값이 있는 슬라이더를 설정하는 코드를 제공합니다. 다시 이 코드를 사용해서 모든 파일을 어지럽히고 싶지 않아서 한 곳에 뒀습니다.
이 스크립트는 webglfundmentals.org를 제외하고는 필요하지 않습니다. live editor 내부에서 사용될 때 화면에 에러 메세지 출력하는 걸 도와줍니다.
이건 2d 수학 함수 묶음입니다. 행렬 수학에 대한 첫 글을 시작했을 때는 인라인으로 만들었지만 너무 복잡해져서 이후에는 일부 예제에 이 스크립트가 포함되고 있습니다.
이건 3d 수학 함수 묶음입니다. 3d에 대한 첫 글을 시작했을 때는 인라인으로 만들었지만 너무 복잡해져서 3d에 대한 두 번째 글 이후부터 이 스크립트가 포함되고 있습니다.