WebGL Shader : Vertex Shader

mango·2023년 8월 15일

🪡WebGL

목록 보기
4/5
post-thumbnail

출처: webglfundamentals.org 사이트
해당 사이트의 내용과 설명이 필요한 개념들을 추가하여 재구성한 내용입니다.


WebGL은 뭔가를 그릴 때마다 2개의 셰이더를 필요로 한다.

바로 Vertex Shader와 Fragment Shader이다.
각각의 셰이더는 함수다.

Vertex Shader와 Fragment Shader는 함께 Shader 프로그램으로 연결된다.

Vertex Shader

Vertex Shader의 역할은 클립 공간 좌표를 생성하는 것으로 아래와 같은 형식이다.

void main() {
  gl_Position = doMathToMakeClipspaceCoordinates
}

셰이더는 정점마다 한 번씩 호출된다.
호출될 때마다 특수 전역 변수, gl_Position을 특정 클립 공간 좌표로 설정해야 한다.

Vertex Shader는 데이터가 필요한데 3가지 방법으로 데이터를 가져올 수 있다.

  1. 속성 (버퍼에서 가져온 데이터)
  2. 유니폼
  3. 텍스처 (픽셀/텍셀 데이터)

속성

가장 일반적인 방법은 버퍼와 속성을 이용하는 것이다.

먼저 버퍼를 만들고

var buf = gl.createBuffer();

버퍼에 데이터를 넣는다.

gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, someData, gl.STATIC_DRAW);

만든 셰이더 프로그램을 통해 초기화 시 속성의 위치를 찾고,

var positionLoc = gl.getAttribLocation(someShaderProgram, "a_position");

렌더링할 때 해당 버퍼에서 속성으로 데이터를 어떻게 가져올지 WebGL에 지시한다.

// 이 속성에 대해 버퍼에서 데이터 가져오기 활성화
gl.enableVertexAttribArray(positionLoc);
 
var numComponents = 3;  // (x, y, z)
var type = gl.FLOAT;    // 32비트 부동 소수점 값
var normalize = false;  // 값 원본 그대로 유지
var offset = 0;         // 버퍼의 처음부터 시작
var stride = 0;         // 다음 정점으로 가기 위해 이동하는 바이트 수
                        // 0 = type과 numComponents에 맞는 스트라이드 사용
 
gl.vertexAttribPointer(positionLoc, numComponents, type, false, stride, offset);

클립 공간 정점을 버퍼에 넣으면 동작한다.

attribute vec4 a_position;
 
void main() {
  gl_Position = a_position;
}

속성의 타입으로는 float vec2 vec3 vec4 mat2 mat3 mat4 를 사용할 수 있다.

유니폼

셰이더 Uniform 은 그리기 호출의 모든 정점에 대해 동일하게 유지되며 Shader에 전달되는 값이다.

간단한 예로 위의 Vertex Shader에 오프셋을 추가해보겠다.

attribute vec4 a_position;
uniform vec4 u_offset;
 
void main() {
  gl_Position = a_position + u_offset;
}

이제 모든 정점을 일정량만큼 오프셋 할 수 있다.
먼저 초기화 시 유니폼의 위치를 찾는다.

var offsetLoc = gl.getUniformLocation(someProgram, "u_offset");

그런 다음 그리기 전에 유니폼을 설정한다.

gl.uniform4fv(offsetLoc, [1, 0, 0, 0]);  // 화면 오른쪽 절반으로 오프셋

유니폼은 여러 타입을 가질 수 있다.
각 타입마다 설정을 위해 함수를 호출해야 한다.

gl.uniform1f (floatUniformLoc, v);                 // float
gl.uniform1fv(floatUniformLoc, [v]);               // float 또는 float 배열
gl.uniform2f (vec2UniformLoc, v0, v1);             // vec2
gl.uniform2fv(vec2UniformLoc, [v0, v1]);           // vec2 또는 vec2 배열
gl.uniform3f (vec3UniformLoc, v0, v1, v2);         // vec3
gl.uniform3fv(vec3UniformLoc, [v0, v1, v2]);       // vec3 또는 vec3 배열
gl.uniform4f (vec4UniformLoc, v0, v1, v2, v4);     // vec4
gl.uniform4fv(vec4UniformLoc, [v0, v1, v2, v4]);   // vec4 또는 vec4 배열
 
gl.uniformMatrix2fv(mat2UniformLoc, false, [  4x element array ])  // mat2 또는 mat2 배열
gl.uniformMatrix3fv(mat3UniformLoc, false, [  9x element array ])  // mat3 또는 mat3 배열
gl.uniformMatrix4fv(mat4UniformLoc, false, [ 16x element array ])  // mat4 또는 mat4 배열
 
gl.uniform1i (intUniformLoc, v);                   // int
gl.uniform1iv(intUniformLoc, [v]);                 // int 또는 int 배열
gl.uniform2i (ivec2UniformLoc, v0, v1);            // ivec2
gl.uniform2iv(ivec2UniformLoc, [v0, v1]);          // ivec2 또는 ivec2 배열
gl.uniform3i (ivec3UniformLoc, v0, v1, v2);        // ivec3
gl.uniform3iv(ivec3UniformLoc, [v0, v1, v2]);      // ivec3 또는 ivec3 배열
gl.uniform4i (ivec4UniformLoc, v0, v1, v2, v4);    // ivec4
gl.uniform4iv(ivec4UniformLoc, [v0, v1, v2, v4]);  // ivec4 또는 ivec4 배열
 
gl.uniform1i (sampler2DUniformLoc, v);             // sampler2D (texture)
gl.uniform1iv(sampler2DUniformLoc, [v]);           // sampler2D 또는 sampler2D 배열
 
gl.uniform1i (samplerCubeUniformLoc, v);           // samplerCube (texture)

배열의 경우에는 배열의 모든 유니폼을 한번에 설정할 수 있다.

// 셰이더
uniform vec2 u_someVec2[3];
 
// 초기화 시
var someVec2Loc = gl.getUniformLocation(someProgram, "u_someVec2");
 
// 렌더링할 때
gl.uniform2fv(someVec2Loc, [1, 2, 3, 4, 5, 6]);  // u_someVec2 전체 배열 설정

다음은 Fragment Shader를 알아보겠다.

profile
https://mangode.tistory.com/

0개의 댓글