들어가기에 앞서..
먼저 GLSL 뭔지 하나도 모르는, 그야말로 아무것도 모르는 분들을 타겟팅한 글이기에, 혹시 GLSL에 대해 어느정도 개념은 알고 계시면 뒤로가기를 누르시는걸 추천드립니다!
GLSL
OpenGL Shading Language로 C언어 기반입니다(컴퓨터 공학과 여러분들 드디어 C언어를 배운 보람을 느낄 수 있습니다..).
GLSL을 통해 OpenGL의 쉐이더를 다룰 수 있습니다.
OpenGL은 GPU인터페이스이기에, 쉽게 말해 GLSL을 통해 GPU에서 실행 가능한 프로그램을 구현할 수 있습니다.
일상적으로 여러분이 보시는 브라우저 화면, 또한 OpenGL을 통해 GPU와 통신하고, 게임, 영상 등.. 가시적인 화면에 대한 모든 어플리케이션은 OpenGL을 통해 GPU와 통신하고 그 결과 화려한 이펙트, 디자인 등, 모든 것들이 가능한 것입니다.
물론 그 모든 과정에 대한 부분은 추후 글에서 하나하나씩 서술하겠습니다.( 저도 몰라요.. 공부해야합니다 )
자 그럼, 일단 하나하나씩 큰 개념들을 까보겠습니다.
What is Shader?
쉐이더는, 구글링하면 장황한 설명들이 나오나, 그냥 GPU에서 돌아가는 프로그램입니다. 대표적으로 앞으로 매우 많이 볼 (Vertex Shader , Fragment Shader)가 있습니다.
일반적으로 앞서 말한것 처럼 Vertex Shader, Fragment Shader 처럼 앞에 붙은 단어에 맞게 구분되며, GPU의 렌더링(Hardware graphics acceleration) 파이프라인을 성공적으로 이루기 위해, 각 프로세스 마다 수행하는 일들이 있습니다.
Shader Structure
아까 GLSL이 C언어 기반이라 말씀드렸죠? 그래서 문법도 C언어랑 유사한점이 많습니다.
C언어처럼 명령형이며, strongly-typed 언어입니다.
1. 먼저 모든 쉐이더의 엔트리는, void인자 , void return main 함수입니다.
void main(){
}
2. C언어와 동일한 흐름제어가 가능합니다.
- for loop
- while
- do...while
- switch
- if...else
- break , continue , return
3. 기본적인 사칙연산 관련 연산자 또한 C와 유사합니다.
- +
- -
- *
- /
- +=
- -=
4. 데이터 타입 (Qualifier)
- (Scalar Type) :: float, int ,bool , uint , double
- (Vector type) :: vec"N", uvec"N", ivec"N", dvec"N", bvec"N"
- u : unsigned
- d : double
- b : boolean
- i : signed
- "N" : 2, 3 ,4 중하나 , (ex) vec3,vec4, ivec3
- (Matrix type) :: mat"N" , mat"N"x"M" ((ex) mat3 , mat4 , mat2x3 )
- (Opaque type) :: Images , Sampler , Atomic counter ( 실제 데이터를 참조하는 값, 포인터와 유사하다 생각하시면 됩니다 )
- Array (배열)
- Struct (구조체)
5. Call by Value
기본적으로, GLSL은 "값에 의한 호출"(call by value)을 지원합니다.
만약 "참조에 의한 호출"(call by reference)를 하고 싶다면 "in", "out" 키워드를 통해 함수 인자로 넘기시면 됩니다.
다음과 같은 방법으로 하시면 됩니다.
void getColorUsingOut(in vec4 color, out vec4 final){
final = color * vec4(0.5);
}
void main(){
vec4 potato;
getColorUsingOut(vec4(1.0) , potato); // potato값이 참조되어 인자로 넘겨집니다.
// potato => vec4(0.5,0.5,0.5,0.5)
gl_FragColor = vec4(1.0, 0.0,0.0,1.0);
}
5. Component-wise operation
GLSL은 기본적으로 각 구성 성분간 연산을 합니다. 다만, 행렬 곱연산의 경우, 일반적인 곱연산을 진행합니다.
vec3(1.0,1.0,1.0) * vec3(0.5,0.5,0.5) => vec3(0.5,0.5,0.5)
vec2(1.0,1.0) * 2 => vec2(2.0,2.0)
mat2(2,2,2,2) * mat2(3 ,3 ,3 ,3 ) => mat2 (12,12,12,12)
만약 행렬에 대해서도 component-wise 연산을 진행하고 싶다면 다음과 같은 방법을 사용하시면 됩니다.
m1 = matrixCompMult(m2, m3);
Shader Parameter
일반적으로 웹 개발쪽 할때, Vertex Shader와 Fragment Shader을 합쳐서 쉐이더 프로그램이라 칭하긴 합니다.
왜냐면 이 두가지 프로세스가 끝나야 가시적으로 볼 수 있기 때문입니다. 특히 webGL 프레임워크 쓰시는 분들은 거의 대부분 Vertex & Fragment Shader만 작성할거라 생각합니다.
비단 위 두가지 쉐이더 뿐만 아니라, 다른 쉐이더들을 실행할 때 여러 파라미터로 실행 할 수 있으나, Fragment & Vertex에 중점을 맞춰 이야기하자면 3가지 파라미터가 있습니다.
1. uniforms
가장 일반적인 방식이며, 전역 변수의 개념이라 생각하셔도 됩니다. 선언 시, Vertex & Fragment Shader모두 같은 값을 읽습니다.
uniform vec4 hello;
2. attributes
Vertex shader 전용입니다. 해당 모델 그 자체에 대한 데이터를 담습니다. (positions ,normals , texture coordintates, etc..)
attrubute vec4 myAttrubute;
3. varying
Vertex shader -> Fragment shader로의 단방향 통신을 위한 파라미터입니다. varying 데이터는 vertex shader에서 다음 파이프라인으로 넘어갈 때, Fragment shader가 이해할 수 있게 변형되어 보내집니다. 즉, 엄밀히 따지자면, Fragment shader가 varying을 통해 받는 데이터는 Vertex shader의 데이터와 완전히 동치다! 라고 말할 순 없습니다.
varying vec4 myVarying;
'WebGL' 카테고리의 다른 글
[WebGL] Hemisphere Lighting (0) | 2023.07.09 |
---|---|
[WebGL] Ambient light (0) | 2023.07.09 |
[WebGL + Three] Textures (텍스쳐 다루기) (0) | 2023.07.02 |
[WebGL + Three.js] GLSL - colors (1) | 2023.03.23 |
[Graphics] Transformation Pipeline (local ~ screen 좌표계) (0) | 2023.03.19 |