본문 바로가기

WebGL

WebGL Fundamentals > Fundamentals : WebGL Shaders and GLSL

https://webglfundamentals.org/webgl/lessons/webgl-shaders-and-glsl.html

 

WebGL Shaders and GLSL

What's a shader and what's GLSL

webglfundamentals.org

https://github.com/Myoungmin/WebGL_Fundamentals

 

GitHub - Myoungmin/WebGL_Fundamentals: WebGL 학습 프로젝트

WebGL 학습 프로젝트. Contribute to Myoungmin/WebGL_Fundamentals development by creating an account on GitHub.

github.com

 

 

 

WebGL Shaders and GLSL

vertex shader와 fragment shader는 함께 program으로 연결된다. 

일반적인 WebGL 앱은 많은 program을 가진다.

 

Vertex Shader

vertex shader의 역할은 클립 공간 좌표를 생성하는 것이다

 

shader는 vertex마다 한 번씩 호출된다.

호출될 때마다 특수 전역 변수, gl_Position을 특정한 클립 공간 좌표로 설정해야 한다.

 

vertex shader가 데이터를 가져오는 3가지 방법.

  • Attributes : buffer에서 가져온 데이터
  • Uniforms : single draw가 호출될 때 모든 vertex에서 동일하게 유지되는 값, 전역변수 처럼 사용한다.
  • Textures : pixels/texels의 데이터

 

1. Attributes

vertex shader가 데이터를 가져오는 가장 일반적인 방법은 buffer와 attribute를 통하는 것이다.

1) 먼저 buffer를 생성한다.

var buf = gl.createBuffer();

2) buffer에 데이터를 넣는다.

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

3) shader program을 통해 초기화 시 attribute 위치를 찾는다.

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

4) Rendering할 때 해당 buffer에서 attribute로 데이터를 어떻게 가져올지 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);

5) 클립 공간 vertex를 buffer에 넣으면, attribute를 통하여 vertex shader에 정상적인 위치에 대한 데이터를 넘겨줄 수 있다.

 

2. Uniforms

그리기 호출의 모든 vertex에 대해 동일하게 유지되며 shader에 전달되는 값.

1) 오프셋 값을 추가하기 위해 vertex shader에서 uniform 선언

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

2) 초기화 시 uniform의 위치를 찾는다.

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

3) 그리기 전에 uniform을 설정한다.

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

 

uniform은 개별 shader program에 속한다.

만약 이름이 같은 uniform을 가진 shader program이 여러 개 있다면, 

두 uniform 모두 고유한 위치와 값을 가진다.

그래서 gl.uniform???을 호출하면 현재 program의 uniform만 설정한다.

현재 program은 gl.useProgram에 전달한 마지막 프로그램이다.

 

4) uniform은 여러 타입을 가질 수 있고, 각 타입마다 설정을 위해 gl.uniform???으로 해당하는 함수를 호출해야 한다.

bool, bvec2, bvec3, bvec4 타입도 있고, gl.uniform?f? 또는 gl.uniform?i? 함수를 사용한다.

 

5) 배열의 경우 배열의 모든 uniform을 한번에 설정할 수 있다.

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

6) 배열의 개별 요소를 설정하고 싶다면 각 요소의 위치를 개별적으로 찾아야 한다.

// 초기화 시 자바스크립트
var someVec2Element0Loc = gl.getUniformLocation(someProgram, "u_someVec2[0]");
var someVec2Element1Loc = gl.getUniformLocation(someProgram, "u_someVec2[1]");
var someVec2Element2Loc = gl.getUniformLocation(someProgram, "u_someVec2[2]");
 
// 렌더링할 때
gl.uniform2fv(someVec2Element0Loc, [1, 2]);  // 요소 0 설정
gl.uniform2fv(someVec2Element1Loc, [3, 4]);  // 요소 1 설정
gl.uniform2fv(someVec2Element2Loc, [5, 6]);  // 요소 2 설정

7) 구조체를 생성하면, 각 필드를 개별적으로 찾아야 한다.

struct SomeStruct {
  bool active;
  vec2 someVec2;
};
uniform SomeStruct u_someThing;

 

개별적으로 위치 확인

 

var someThingActiveLoc = gl.getUniformLocation(someProgram, "u_someThing.active");
var someThingSomeVec2Loc = gl.getUniformLocation(someProgram, "u_someThing.someVec2");

 

 

 

Fragment Shader

fragment shader의 역할은 래스터화되는 현재 픽셀의 색상을 제공하는 것이다.

 

fragment shader는 각 픽셀마다 한 번씩 호출된다.

호출될 때마다 특수 전역 변수, gl_FragColor를 어떤 색상으로 설정해줘야 한다.

 

fragment shader가 데이터를 가져오는 3가지 방법.

  • Uniforms : single draw가 호출될 때 모든 vertex에서 동일하게 유지되는 값, 전역변수 처럼 사용한다.
  • Textures : pixels/texels의 데이터
  • Varyings : vertex shader에서 전달되고 보간된 데이터

 

1. Textures in Fragment Shaders

1) shader의 texture에서 값을 가져오면 sampler2D uniform을 생성하고, 값을 추출하기 위해 GLSL 함수 texture2D를 사용한다.

precision mediump float;
 
uniform sampler2D u_texture;
 
void main() {
  vec2 texcoord = vec2(0.5, 0.5)  // 텍스처 중앙에서 값 가져오기
  gl_FragColor = texture2D(u_texture, texcoord);
}

2) texture에서 나오는 데이터는 수많은 설정에 따라 달라진다.

아래는 최소한의 texture 데이터를 생성하고 넣는 예시이다.

 

var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
var level = 0;
var width = 2;
var height = 1;
var data = new Uint8Array([
  255, 0, 0, 255,   // 빨강 픽셀
  0, 255, 0, 255,   // 초록 픽셀
]);
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

3) 초기화 시 shader program의 uniform 위치를 찾는다.

var someSamplerLoc = gl.getUniformLocation(someProgram, "u_texture");

4) Rendering할 때 texture unit에 texture를 바인딩한다.

var unit = 5;  // 텍스처 유닛 선택
gl.activeTexture(gl.TEXTURE0 + unit);
gl.bindTexture(gl.TEXTURE_2D, tex);

5) texture를 바인딩한 unit이 무엇인지 shader에 알려준다.

gl.uniform1i(someSamplerLoc, unit);

 

2. Varyings

vertex shader에서 fragment shader로 값을 전달하는 방법.

 

varying을 사용하려면 vertex shader와 fragment shader 양쪽에 서로 동일한 varying을 선언해야 한다.

 

각 vertex마다 vertex shader의 varying에 특정 값으로 설정하고, WebGL이 픽셀을 그릴 때 설정한 값들 사이를 보간한다.

fragment shader에서 보간의 결과값이 대응되는 varying으로 전달된다.

 

GLSL

  • Graphics Library Shader Language의 약자로 shader가 작성되는 언어이다.
  • 그래픽 래스터화에 일반적으로 필요한 수학적 계산을 수행하도록 설계되었다.
  • 예를 들어 각각 2개의 값, 3개의 값, 4개의 값을 나타내는 vec2, vec3, vec4 같은 타입들이 내장되어 있다. 
  • 마찬가지로 2x2, 3x3, 4x4 행렬을 나타내는 mat2, mat3, mat4가 있다.

 

 

 

 

 

 

 

https://myoungmin.github.io/WebGL_Fundamentals/

 

WebGL_Fundamentals

WebGL이란? WebGL은 Web Graphics Library의 약자로 웹상에서 2D 및 3D 그래픽을 렌더링하기 위한 로우 레벨 Javascript API.

myoungmin.github.io