기본 데이터 타입 개요
GLSL 은 그래픽 파이프라인 연산을 위해 정의된 다양한 타입을 지원합니다. 각 타입은 특정 용도에 따라 설계되었습니다.
| 타입 | 설명 |
|---|---|
| void | 값을 반환하지 않는 함수의 리턴 타입으로 사용됩니다. |
| bool | 논리형 자료로 true 또는 false 값을 가집니다. |
| int | 부호가 있는 정수입니다. |
| float | 단일 부호浮點數 (scalar) 입니다. |
| vec2 ~ vec4 | n 차원 부호浮點數 벡터입니다. |
| bvec2 ~ bvec4 | n 차원 불린 벡터입니다. |
| ivec2 ~ ivec4 | n 차원 정수 벡터입니다. |
| mat2 ~ mat4 | 각각 2x2, 3x3, 4x4 크기의 행렬입니다. |
| sampler2D | 2 차원 텍스처를 참조할 때 사용하는 커먼입니다. |
| samplerCube | 큐브 맵 형태의 텍스처에 접근합니다. |
구조체 및 배열 관리
C 언어와 유사하게 구조체 struct 를 정의할 수 있으며, 이를 통해 데이터를 묶어 관리할 수 있습니다. 배열은 현재 1 차원만 지원되며, 구조체의 멤버가 될 수도 있습니다.
struct Mesh {
float positions[3];
};
float myArray[5];
벡터 성분 접근 (Swizzling)
GLSL 의 벡터 타입 (vec2`, vec3`, vec4) 은 좌표계나 색상 공간 등을 표현할 때 자주 사용됩니다. 이를 위해 직관적인 성분 접근 방식을 제공합니다.
- 좌표 계열:
x,y,z,w조합 가능 - 색상 계열:
r,g,b,a조합 가능 - 텍스처 계열:
s,t,p,q조합 가능
아래 코드는 동일한 벡터 데이터에 대해 서로 다른 접근 방식과 재배열 예시입니다.
// 초기 데이터 설정
vec4 baseVec = vec4(0.1, 0.2, 0.3, 1.0);
// 개별 성분 접근
float xVal = baseVec.x; // 0.1
float rVal = baseVec.r; // 0.1
int indexVal = int(baseVec[0]); // 0 (implicit cast 주의)
// 다중 성분 슬라이싱
vec3 xyzPart = baseVec.xyz; // vec3(0.1, 0.2, 0.3)
vec3 rgbPart = baseVec.rgb; // vec3(0.1, 0.2, 0.3)
// 2 차원으로 축소
vec2 stCoords = baseVec.st; // vec2(0.1, 0.2)
vec4 fullCopy = baseVec.stpq; // 원본과 동일
연산 우선순위 및 규칙
연산자는 결합성과 우선순위가 명확히 정의되어 있습니다. 괄호 () 는 가장 높은 우선순위를 가지며, 대입 연산자는 가장 낮습니다.
| 우선순위 | 연산자 | 예시 | 결합성 |
|---|---|---|---|
| 1 | () | (a + b) * c | - |
| 2 | [] . ++ -- | a[i], obj.prop, a++ | L → R |
| 3 | + - ! | !bool, -val | R → L |
| 4 | * / | a * b, c / d | L → R |
| 5 | + - | a + b, c - d | L → R |
| 7 | < > | a > b | L → R |
| 8 | == != | a == 0 | L → R |
| 12 | && | a && b | L → R |
| 14 | || | a || b | L → R |
| 15 | ? : | cond ? true : false | L → R |
| 16 | = += -= | a += 1 | L → R |
이 때 左值 (L-value)는 메모리 주소를 의미하고, 右值 (R-value)는 구체적인 데이터 값을 의미합니다. 연산 시에는 명시적 타입 변환이 필수적입니다.
타입 간 연산 제약
GLSL 은 C/C++ 와 달리 자동 형변환 (Implicit Conversion) 을 허용하지 않습니다. 즉, 좌항과 우항의 타입이 완전히 일치해야 합니다.
// 에러 발생 사례
int invalidInt = 2.0; // float 과 int 혼합 오류
float invalidFloat = 2; // implicit cast 없음
bool invalidBool = 0; // 정수를 bool 로 바로 할당 불가
// 올바른 사용법 (명시적 형변환)
int correctInt = int(2.0); // float 에서 int 로 변환
float correctFloat = float(2);
bool t = bool(1.5); // 비 0 값은 true
복합 타입 연산 전략
- Scalar & Vector: 스칼라 값과 벡터 연산 시, 스칼라 값은 모든 성분에 곱해지거나 더해지는 요소wise(逐分量) 연산이 수행됩니다.
- Vector & Vector: 두 벡터 간 연산도 마찬가지로 해당 인덱스의 성분끼리 계산되지만, 행렬 곱셈과는 다릅니다.
- Matrix Multiplication: 행렬 간 또는 벡터와 행렬 간 연산은 선형대수학적 행렬 곱셈 규칙을 따릅니다.
vec3 p = vec3(1.0, 2.0, 3.0);
mat3 m = mat3(1.0);
float factor = 10.0;
// 요소별 연산
vec3 scaled = p * factor;
mat3 scaledMat = m * factor;
// 행렬 곱셈 (요소별 X 아님)
vec2 v = vec2(10., 20.);
mat2 mat = mat2(1., 2., 3., 4.);
vec2 result = mat * v; // 행렬-벡터 곱셈
메모리 저장 지정자 (Storage Qualifiers)
변수의 유효 범위와 데이터 흐름을 제어하는 키워드입니다.
| 지정자 | 특징 |
|---|---|
| uniform | 호스트 측에서 전달되고 셰이더 내에서 읽기 전용인 상수. |
| attribute | 버텍스 셰이더 전용, 호스트로부터 입력받는 데이터 (위치, 법선 등). |
| varying | 버텍스와 프래그먼트 셰이더 간의 데이터를 중계용. |
| const | 초기화 후 변경 불가 (지역 변수에도 적용 가능). |
uniform은 렌더링 루프 외부에서 값을 업데이트하여 조명이나 매트릭스를 전달받을 때 쓰이고, attribute는 메쉬 데이터처럼 매 프레임마다 변하지 않는 경우에도 버텍스 단위로 다르게 처리해야 할 때 사용됩니다.
함수 파라미터 제어
파라미터 전달 방식을 명시적으로 지정할 수 있습니다.
- in: 복사 전달 (기본값), 함수 내 수정이 원값에 반영되지 않음.
- out: 반환용, 함수 호출 시 원값을 무시하고 내부 값만 받아옴.
- inout: 복사 후 수정하여 다시 복제해옴, 원본 값에 영향을 줌.
void updateSize(inout float val) {
val *= 2.0; // 외부 변수 크기 변경됨
}
void logMsg(out string msg) {
msg = "Done"; // 결과 출력용
}
정밀도 및 최적화
디바이스에 따른 성능 차이로 인해 세 종류의 정밀도 한정자가 존재합니다.
highp: 고정밀도 연산mediump: 중간 정밀도 (Default)lowp: 저정밀도 (모바일 배터리 절감용)
편의상 파일 최상단 또는 프래그먼트 셰이더 시작 부분에 기본 정밀도를 설정할 수 있습니다.
#ifdef GL_ES
precision mediump float;
#endif
여러 변수가 섞여 있을 때는 가장 높은 정밀도를 가진 식이 전체 연산 정밀도를 결정합니다.
내장 변수 및 시스템 정보
하드웨어 상태에 대한 정보를 가져오는 상수가 제공되며, 일부는 버전 (1.30 이전) 에 따라 Deprecated 될 수 있습니다.
| 내장 변수 | 설명 |
|---|---|
gl_Position | 출력 (Vertex): 클립 좌표로 변환됨. |
gl_FragColor | 출력 (Fragment): 최종 픽셀 색상. |
gl_FragCoord | 입력 (Fragment): 화면상 좌표. |
gl_MaxVertexAttribs | 사용 가능한 최대 어트리뷰트 개수. |
gl_DepthRange | 깊이 범위에 대한 Uniform 상태 변수. |
또한 discard 문을 사용하면 해당 픽셀의 렌더링을 중단하여 뒤쪽 연산이나 프래그먼트 출력을 생략할 수 있습니다.
유틸리티 함수 라이브러리
GLSL 은 표준 수학 라이브러리를 광범위하게 제공합니다.
일반 수학
| 함수 | 기능 |
|---|---|
abs(x) | 절대값 |
max(a, b), min(a, b) | 최댓값, 최솟값 비교 |
clamp(val, min, max) | 값을 범위 내에 제한 |
mix(a, b, t) | 선형 보간 (t가 0 일 때 a, 1 일 때 b) |
step(edge, x) | 경계선 임계값 기반 스텝 함수 |
fract(x) | 소수 부분 추출 |
삼각 및 기하학
| 함수 | 기능 |
|---|---|
sin/cos/tan | 삼각함수 (단위: 라디안) |
length(v) | 벡터 길이 계산 |
normalize(v) | 단위 벡터로 변환 |
dot(v1, v2) | 내적 계산 |
cross(v1, v2) | 외적 계산 (vec3 전용) |
reflect(I, N) | 반사 벡터 생성 |
텍스처 샘플링
텍스처 조회 함수는 셰이더 종류와 타겟 (프로젝션 유무) 에 따라 나뉩니다.
// 일반적인 2D 샘플링
vec4 color = texture(sampler2D tex, vec2 uv);
// lod (Level Of Detail) 제어 포함
vec4 mipMapSample = texture2DLod(tex, uv, float_lod);
// 큐브 맵 샘플링
vec4 cubeSample = textureCube(samplerCube cubemap, vec3 dir);
실무 예시 코드
다음은 기본적인 조명이 적용된 모델 렌더링을 위한 코드로, 버텍스와 프래그먼트 셰이더 사이의 데이터 흐름을 보여줍니다.
// === 버텍스 셰이더 ===
uniform mat4 u_matrix;
uniform vec3 u_lightDir;
attribute vec4 a_pos;
attribute vec3 a_normal;
attribute vec2 a_uv;
varying float v_diffuse;
varying vec2 v_uv;
void main() {
//的法线变换
vec3 normal = normalize(mat3(u_matrix) * a_normal);
// Diffuse 계산 (Max Dot)
float diff = max(dot(normalize(u_lightDir), normal), 0.0);
v_diffuse = diff;
v_uv = a_uv;
gl_Position = u_matrix * a_pos;
}
// === 프래그먼트 셰이더 ===
precision mediump float;
uniform sampler2D u_texture;
uniform vec3 u_ambient;
varying float v_diffuse;
varying vec2 v_uv;
void main() {
vec4 texCol = texture2D(u_texture, v_uv);
// Final Color = Texture * (Light * Diffuse + Ambient)
vec3 finalColor = texCol.rgb * (v_diffuse * u_ambient + vec3(0.2));
gl_FragColor = vec4(finalColor, 1.0);
}