본문 바로가기
Graphics/GLSL

03. Uniforms, gl_FragCoord

by Lein_ 2021. 12. 30.

Uniforms

지금까지 GPU가 많은 수의 병렬 스레드를 관리하는 방법을 살펴보았다. 각 스레드는 전체 이미지의 일부에 색상을 할당하는 역할을 한다. 각 병렬 스레드는 다른 스레드가 무얼 하고 있는지 알 수 없다(blind). 하지만 CPU에서 모든 스레드로 입력값을 전송할 수는 있다. 그래픽카드의 아키텍처 상 이러한 입력은 모든 스레드들에 동일하게 들어가며, 읽기 전용으로 설정된다. 각 스레드는 읽을 수는 있으나 변경할 수 없는 동일한 데이터를 수신한다.

 

이러한 입력을 uniform이라고 하며 다음과 같은 타입에 대해 대부분 지원한다.

float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D, samplerCube

uniform은 보통 디폴트 부동소수점 정밀도를 할당한 후에 셰이더 상단에 해당 타입으로 정의된다. 다음 코드를 보면 무슨 말인지 이해가 될 것이다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;  // Canvas size (width,height)
uniform vec2 u_mouse;       // mouse position in screen pixels
uniform float u_time;       // Time in seconds since load

 

uniform은 CPU와 GPU 사이를 이어주는 징검다리라고 보면 된다. 이름은 구현마다 다르지만 예제에서는 u_time (셰이더가 시작된 후 초 단위의 시간), u_resolution (화면의 크기) , u_mouse(화면 내 에서의 마우스의 위치). uniform에 대한 이름은 GLSL 프로그래밍을 지원하는 사이트마다 다르므로 예제를 보고 파악할 필요가 있다. 예를 들어

 

uniform vec3 iResolution;   // viewport resolution (in pixels)
uniform vec4 iMouse;        // mouse pixel coords. xy: current, zw: click
uniform float iTime;        // shader playback time (in seconds)

 

The Book of Shaders에서는 이름이 u_resolution, u_mouse, u_time인 반면 Shadertoy에서는 각각 iResolution, iMouse, iTime임을 알 수 있다. 또다른 사이트인 twigl.app 에서는 다음과 같다.

 

uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;

 

GLSL 프로그래밍 사이트는 다양하므로 예제를 보고 파악해야 한다는 것을 명심하자.

 

다음 코드는 셰이더가 실행되기 시작한 이후 초 단위인 u_time을 sin 함수와 함께 사용하여 화면에 애니메이션을 적용한다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform float u_time;

void main() {
	gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);
}

GPU는 하드웨어 가속화 된 각도, 삼각법, 지수 기능을 갖추고 있다. 이러한 함수의 예로 sin(), cos(), tan(), acos(), atan(), pow(), exp(), log(), sqrt(), abs(), floor(), ceil(), fract(), mod(), max(), clamp() 등이 있다.

해당 에디터의 레퍼런스는 https://thebookofshaders.com/glossary/ 를 참고하자.

 

셰이더 코딩을 공부하는 가장 좋은 방법은 작성한 예제를 조금씩 변형하며 갖고 노는 것이다. 우연히 바꾼 것으로 예쁜 그림이 나오는 경우도 꽤 흔하다. 색 변화가 거의 인지되지 않을 정도로 천천히 바뀌게 하거나, 깜빡임(flickering)이 안 보일 때까지 속도를 올리거나, 세 개의 채널을 각각 다른 frequency로 재생시키거나 다양한 방법이 있다.


gl_FragCoord

GLSL이 default output인 vec4 gl_FragColor를 제공하는 것과 마찬가지로, default input으로 vec4 gl_FragCoord도 제공한다. 이 입력은 활성 스레드가 작업 중인 픽셀의 좌표를 가지고 있다. 유일하게 스레드마다 다른 값을 갖고 있기 때문에 uniform이 아니며 gl_FragCoord를 가변(varying)으로 취급한다.

 

#ifdef GL_ES
precision mediump float;
#endif

uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;

void main() {
	vec2 st = gl_FragCoord.xy/u_resolution;
	gl_FragColor = vec4(st.x,st.y,0.0,1.0);
}

위 코드에서 각 픽셀의 좌표를 화면 해상도로 나눠 좌표를 정규화하고 있다. 이렇게 하면 값이 0.0에서 1.0 사이로 되며, 이 값을 RGBA 채널에 쉽게 매핑할 수 있다.

 

GLSL에서는 디버깅을 할 수 있는 방법이 많지 않다. 눈에 튀는 색상을 지정해 디버깅하는 것이 주로 사용하는 방법이다.

 

GLSL의 좌표계에서 원점은 좌측하단이다. 즉 (0.0, 0.0)이 왼쪽 밑이다. x축 양의 방향으로 증가, y축 양의 방향으로 증가이다.

 

 u_time과 u_mouse 좌표를 사용하여 색상 패턴을 바꾸는 흥미로운 방법을 상상할 수 있는가? 다양한 방법들을 구상해보자.

'Graphics > GLSL' 카테고리의 다른 글

06. Shapes  (0) 2022.01.02
05. Colors  (0) 2021.12.31
04. Shaping functions  (0) 2021.12.30
02. Hello world!  (0) 2021.12.29
01. Shader란 무엇인가?  (0) 2021.12.29

댓글