OpenGL Shader – 12

GLSL을 위한 OpenGL 설정 – Uniform 변수
원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?ogluniform

Uniform 변수는 오로지 Primitive에 의해 변경될 수 있는 변수인데, glBegin과 glEnd 사이에서는 변경될 수 없다. 이러한 이유로해서 버텍스의 속성을 위해서는 사용될 수 없다. 만약 버텍스의 속성에 대한 변수를 원한다면, 이 섹선 바로 다음 섹션인 Attribute 변수를 보면된다. Uniform 변수는 Primitive나 프레임 또는 전체 장면 동안에 유지해야할 변수에 적당하다. Uniform 변수는 버텍스 쉐이더나 프레그먼트 쉐이더에서 읽을 수 있지만 쓸수는 없다.

Uniform 변수를 정의하는 가장 먼저 해야할 일은 변수에 대한 메모리 위치를 얻는 것이다. 쉐이더 프로그램이 링크된 후에야 변수에 대한 메모리 정보를 얻어오는 것이 가능하다는 점에 주의하기 바란다. 몇몇의 그래픽 카드 드라이버에서는 메모리의 위치를 얻어오기 위해 먼저 glUseProgram(OpenGL 2.0) 또는 glUseProgramObjectARB(ARB 확장)을 호출해야할 경우도 있다는 점을 주의하자.

변수를 처리할때 OpenGL 2.0과 ARB 확장은 매우 유사한 문법을 가지고 있다. 기본적으로 OpenGL 2.0 함수의 뒤에 ARB를 붙이면 ARB 확장 함수가 되는 경우가 많다.

다음 OpenGL 2.0형태의 함수는 주어진 이름에 대한 Uniform 변수(쉐이더 안에서 정의)의 위치를 얻어온다.

GLint glGetUniformLocation(GLuint program, const char *name);
Parameters:
program – 프로그램의 핸들
name – 변수의 이름

ARB 확장의 형태는 다음과 같다.

GLint glGetUniformLocationARB(GLhandleARB program, const char *name);
Parameters:
program – 프로그램의 핸들
name – 변수의 이름

반환값이 바로 변수의 위치인데, 이 변수에 값을 할당할 수 있다. Uniform 변수에 값을 할당하는 함수군은 다음과 같다.

void glUniform1f(GLint location, GLfloat v0);
void glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void glUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void glUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);

또는

GLint glUnform{1,2,3,4}fv(GLint location, GLsizei count, GLfloat *v);

Parameters:
location – 이전에 구한 변수의 위치
v0, v1, v2, v3 – 실수값
count – 배열에서 요소의 수
v – 실수 배열

ARB 확장의 경우는 다음과 같다.

void glUniform1fARB(GLint location, GLfloat v0);
void glUniform2fARB(GLint location, GLfloat v0, GLfloat v1);
void glUniform3fARB(GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
void glUniform4fARB(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);

또는

GLint glUnform{1,2,3,4}fvARB(GLint location, GLsizei count, GLfloat *v);

Parameters:
location – 이전에 구한 변수의 위치
v0, v1, v2, v3 – 실수값
count – 배열에서 요소의 수
v – 실수 배열

위의 함수는 실수타입의 경우이고 정수타입도 가능한데,  “f”를 “i”로 바꾸기만하면된다. 하지만  boolean 타입에 대한 함수는 없다. Boolean에 대한 경우가 필요하다면 실수나 정수형 함수에 대해서 false의 경우 0으로 할당하여 사용하면 된다. uniform 변수의 배열을 가질 경우, 벡터로 사용될 수 있다.

sampler 변수에 대해서는 OpenGL 2.0에서는 sampler의 배열을 설정하여 glUniform1i나 glUniform1iv를 사용할 수 있으며 ARB 확장의 경우 glUniform1iARB나 glUniform1ivARB를 사용하면 된다.

GLSL에서 매트릭스 데이터 타입도 사용이 가능하며 이 타입에 대한 함수는 다음과 같다.

GLint glUniformMatrix{2,3,4}fv(GLint location, GLsizei count, GLboolean transpose, GLfloat *v);
Parameters:
location – 이전에 질의한 위치
count – 행렬의 개수. 단일 매트릭스라면 1이고 n 매트릭스 배열이라면 n
transpose – 전치 행렬인지 여부이며 1인 경우 Row 방향으로 배열된 행렬이며 0인 경우 Column 방향으로 정렬된 행렬을 의미함
v – 실수 배열

ARB 확장의 경우는 아래와 같다.

GLint glUniformMatrix{2,3,4}fvARB(GLint location, GLsizei count, GLboolean transpose, GLfloat *v);
Parameters:
location – 이전에 질의한 위치
count – 행렬의 개수. 단일 매트릭스라면 1이고 n 매트릭스 배열이라면 n
transpose – 전치 행렬인지 여부이며 1인 경우 Row 방향으로 배열된 행렬이며 0인 경우 Column 방향으로 정렬된 행렬을 의미함
v – 실수 배열

주의할 것은 위의 함수들을 통해 설정된 Uniform 변수의 값들은 쉐이더 프로그램이 다시 링크될동안 유지된다. 일단 새로운 링크 처리가 수행되면 이 값들은 모두 0으로 재설정된다.

간단한 소스코드를 살펴보자.  만약 다음과 같은 변수를 사용하는 쉐이더가 있다고 해보자.

uniform float specIntensity;
uniform vec4 specColor;
uniform float t[2];
uniform vec4 colors[3];

OpenGL 2.0 어플리케이션에서는, 변수 설정을 위한 코드는 다음과 같을 것이다.

GLint loc1,loc2,loc3,loc4;
float specIntensity = 0.98;
float sc[4] = {0.8,0.8,0.8,1.0};
float threshold[2] = {0.5,0.25};
float colors[12] = {0.4,0.4,0.8,1.0,
                    0.2,0.2,0.4,1.0,
                    0.1,0.1,0.1,1.0};

loc1 = glGetUniformLocation(p,"specIntensity");
glUniform1f(loc1,specIntensity);

loc2 = glGetUniformLocation(p,"specColor");
glUniform4fv(loc2,1,sc);

loc3 = glGetUniformLocation(p,"t");
glUniform1fv(loc3,2,threshold);

loc4 = glGetUniformLocation(p,"colors");
glUniform4fv(loc4,3,colors);

위의 코드에 대한 ARB 확장 형태는 다음과 같다.

GLint loc1,loc2,loc3,loc4;
float specIntensity = 0.98;
float sc[4] = {0.8,0.8,0.8,1.0};
float threshold[2] = {0.5,0.25};
float colors[12] = {0.4,0.4,0.8,1.0,
                    0.2,0.2,0.4,1.0,
                    0.1,0.1,0.1,1.0};

loc1 = glGetUniformLocationARB(p,"specIntensity");
glUniform1fARB(loc1,specIntensity);

loc2 = glGetUniformLocationARB(p,"specColor");
glUniform4fvARB(loc2,1,sc);

loc3 = glGetUniformLocationARB(p,"t");
glUniform1fvARB(loc3,2,threshold);

loc4 = glGetUniformLocationARB(p,"colors");
glUniform4fvARB(loc4,3,colors);

위의 예에 대한 전체 코드의 샘플은 다음 링크를 통해 다운로드 받기 바란다.

t나 colors 변수의 경우와 specColor의 4개의 값을 가진 벡터를 설정하는 것과 같은 배열을 설정하는 부분을 주의깊게 보기 바란다. count 인자(glGetUniform{1,2,3,4}fv)의 가운데 인자)는 쉐이더에서 선언된 배열 요소의 개수를 의미하지 OpenGL에서 선언된 요소의 개수를 의미하는 것이 아니다. 이것이 specColor가 4개의 값을 가지고 있음에도, glUniform4fv 함수의 인자 중 count는 1로 설정된 이유인데, specColor는 쉐이더에서 하나의 벡터로 선언되었기 때문이다. specColor 변수를 설정하기 위한 다른 방법은 다음과 같다.

loc2 = glGetUniformLocation(p,"specColor");
glUniform4f(loc2,sc[0],sc[1],sc[2],sc[3]);

배열 안의 변수의 위치를 얻기 위해 GLSL이 제공하는 다른 방법이 또 있는데, 예를들면.. t[1]과 같은 변수의 위치를 얻기 위해 다음과 같이 하면 된다.

loct0 = glGetUniformLocation(p,"t[0]");
glUniform1f(loct0,threshold[0]);

loct1 = glGetUniformLocation(p,"t[1]");
glUniform1f(loct1,threshold[1]);

glGetUniformLocation에서 ‘[]’를 사용해서 원하는 요소의 구성 변수를 지정되는 방법을 사용해서 원하는 바를 얻는다.

위의 경우에 대한 ARB 확장은 ARB를 함수 이름 뒤에 추가하면 동일하므로 여기에서는 생략하겠다.

OpenGL Shader – 11

GLSL을 위한 OpenGL 설정 – OpenGL에서 Shaders로의 통신
원문 : http://www.lighthouse3d.com/opengl/glsl/index.php?oglvariables

OpenGL 어플리케이션은 쉐이더와 통신할 수 있는 몇가지 방법을 가지고 있다. Note that this is a one way communication through, since the only output from a shader is to render to some targets, usually the color and depth buffers.

쉐이더는 OpenGL 상태의 일부에 접근할 수 있으므로, 어플리케이션이 OpenGL 상태의 일부를 변경하면, 쉐이더에서 이 변경된 상태를 접근할 수 있음으로 해서 효과적으로 통신을 할 수 있게 된다. 그래서 만약에 어플리케이션이 쉐이더에게 빛의 색을 전달한다고 할때,  고정기능에서 정상적으로 했던 것처럼  쉐이더는 OpenGL의 상태를 쉽게 변경할 수 있다.

그러나, OpenGL 상태의 사용이 쉐이더에 대한 값을 설정하는 항상 가장 직관적인 방법은 아니다. 예를들어서, 에니메이션의 수행 초과 시간을 전달해주는 변수를 요구하는 쉐이더가 있다고 해보자. 이런 예에서 사용할 목적에 적당한 OpenGL 상태 변수가 없다. 사실, 사용하지 않는 빛과 연관된 상태 변수를 이런 목적으로도 사용할 수 있으나, 매우 직관적이지 못하다.

다행히, GLSL은 쉐이더와 OpenGL 어플리케이션과의 통신을 위한 변수를 사용자가 정의할 수 있다. 변수에 적당한 이름, 위의 예에 적당한 ‘timeElapsed’이라는 이름을 붙이고 계속 사용할 수 있다.

GLSL은 2가지 종류의 변수 평가자를 가지고 있다. (이후의 섹션에서 더 많은 평가자를 소개할 것이다)

  • Uniform
  • Attribute

쉐이더 안에서 정의된, 위의 평가자에 대한 변수는 읽기 전용이다. 이후의 섹션에서는 이런 변수를 언제 사용하고 어떻게 사용하는지에 대해서 자세히 살펴보겠다.

변수를 쉐이더에게 전달하는 또다른 방법이 있는데. 바로 텍스쳐를 이용하는 것이다. 텍스쳐는 반드시 이미지를 표현해야만 하는 것은 아니다. 텍스쳐는 데이터의 배열로써 해석될 수 있다. 사실, 텍스쳐 데이터가 설사 이미지라도 할지라도, 쉐이더를 사용하는 여러분이 텍스쳐 데이터를 어떻게 해석할 것인지 결정할 수 있다. 이런 용도로써의 텍스쳐의 사용은 이 섹션의 영역을 벗어나므로 설명하지는 않겠다.