OpenGL 쉐이딩 언어(GLSL) 핵심 기능 정리

기본 데이터 타입 개요

GLSL 은 그래픽 파이프라인 연산을 위해 정의된 다양한 타입을 지원합니다. 각 타입은 특정 용도에 따라 설계되었습니다.

타입 설명
void값을 반환하지 않는 함수의 리턴 타입으로 사용됩니다.
bool논리형 자료로 true 또는 false 값을 가집니다.
int부호가 있는 정수입니다.
float단일 부호浮點數 (scalar) 입니다.
vec2 ~ vec4n 차원 부호浮點數 벡터입니다.
bvec2 ~ bvec4n 차원 불린 벡터입니다.
ivec2 ~ ivec4n 차원 정수 벡터입니다.
mat2 ~ mat4각각 2x2, 3x3, 4x4 크기의 행렬입니다.
sampler2D2 차원 텍스처를 참조할 때 사용하는 커먼입니다.
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, -valR → L
4* /a * b, c / dL → R
5+ -a + b, c - dL → R
7< >a > bL → R
8== !=a == 0L → R
12&&a && bL → R
14||a || bL → R
15? :cond ? true : falseL → R
16= += -=a += 1L → 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);
}

태그: GLSL OpenGL Shader Programming Graphics Engine WebGL

8월 25일 06:45에 게시됨