-
Notifications
You must be signed in to change notification settings - Fork 4
CH08 Vertex Shaders
이 장에서는 OpenGL ES 3.0 의 Programmable 한 Vertex Pipeline 에 대해 설명합니다.
![1](https://private-user-images.githubusercontent.com/43023361/268558972-59d5a54b-e293-4883-acf5-064aa44b7b7e.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkwMDg4NDEsIm5iZiI6MTczOTAwODU0MSwicGF0aCI6Ii80MzAyMzM2MS8yNjg1NTg5NzItNTlkNWE1NGItZTI5My00ODgzLWFjZjUtMDY0YWE0NGI3YjdlLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjA4VDA5NTU0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQyZDhiNWZlNmRlMjU5YjBjMGNjZDg5ZGEyNTQ2ZDg0Yjc1OGUxNDI2MTI0ZWRiODUyMjY2MzkxY2IyOGM0OWYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.WmTWloGRDyZ-JtZ2YNLR6Bq9083H_Pb9qFDlNXezwOE)
위의 그림은 전체 OpenGL ES 3.0 의 Grapic pipeline 을 설명합니다. 회색 상자는 프로그래밍이 가능한 단계를 표시합니다.
이 장에서는 Vertex Shader 에 대해서 설명합니다.
Vertex Shader 는 행렬을 사용해 위치를 변환하고, 조명 방정식을 계산해 광원을 생성하는등, Vertex 기반 연산을 수행하는데 사용할 수 있습니다.
Vertex Shader 는 vertex 를 연산하기 위한 범용적인 프로그래밍이 가능한 방법들을 제공합니다.
아래는 Vertex Shader 의 입-출력을 표시합니다.
![2](https://private-user-images.githubusercontent.com/43023361/268558980-70bce3e5-f8da-46ca-9d14-cebdd0d39673.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MzkwMDg4NDEsIm5iZiI6MTczOTAwODU0MSwicGF0aCI6Ii80MzAyMzM2MS8yNjg1NTg5ODAtNzBiY2UzZTUtZjhkYS00NmNhLTlkMTQtY2ViZGQwZDM5NjczLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMDglMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjA4VDA5NTU0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWM4MzdiOTIxN2YzMzRiNGFhOTFmMzllMzI5MGZlNDlhYmVkYmFjMGMxODk1N2IxYWYwODY3MzVkN2UwNDIzZGMmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.xQh5UZQPkywH-MVBtrx_JyOcq-49Jn9y47RI6Br1nXM)
Vertex Shader의 입력은,
- Vertex array를 사용하며 제공되는 per-vertex attributes Data 입니다.
- Uniform, Uniform buffer - Vertex Shader 가 사용하는 상수 데이터입니다.
- Sampler - Vertex Shader 에서 사용하는 텍스처를 나타내는 특정 유형의 Uniform 입니다.
- Shader program - Shader 에서 수행될 작업을 지시하는 프로그램 소스코드.
Vertex Shader 의 출력을 vertex shader output variables 라고 부릅니다.
primitive rasterization 단계에서 이 변수들은 생성된 각 primitive 에 대해 계산되어 fragment shader에 입력으로 전달됩니다.
vertex shader의 내장 변수들은 다음 3가지 형태로 분류될 수 있습니다.
- special variable (vertex shader의 input/output으로 쓰임)
- uniform state (예컨대 depth range)
- constants (예컨대 attributes 갯수 최대값, vertex shader output 변수 갯수 최대값, uniform 갯수 최대값)
Built-In Special Variables
OpenGL ES 3.0은 내장(built-in) special variable들이 있습니다. 위에서 언급했듯이 vertex shader의 input으로 쓰이거나, 또는 output으로 쓰여 fragment shader의 input이나 output에 사용됩니다. 다음 내장 special variable들이 vertex shader에서 사용가능합니다.
gl_VertexID
: 정점에 대한 정수 인덱스를 저장하는 입력 변수.
gl_InstanceID
: glDraweArraysInstance 와 같은 instanced draw command 에서 현재 기본 primitive 의 index 을 가지고 있는 입력 변수.
현재 primitive 가 인스턴스화 된 draw command 에서 시작되지 않은 경우, 0의 값을 가집니다.
gl_Position
: Clip coordinates로 정점 위치를 출력할 때 사용됩니다. 이 값은 clipping, viewport 단계에서 순서대로 primitive들이 clipping되고 vertex위치를 clip coordinate에서 screen coordinates로 바꾸어 집니다. highp로 정의된 float형 변수입니다.
gl_PointSize
: 포인트 스프라이트의 크기를 픽셀 단위로 작성하는데 사용됩니다.
gl_FrontFacing
: 랜더링 중, Vertex Shader에 의해서 생성된 vertex position값들과 Primitive 유형에서 생성된 위치 값을 기반으로 생성되는 변수.
Built-In Uniform State
Vertex Shader 내부에서 사용할 수 있는 유일한 내장 Uniform State 는 Depth Range Parameter 입니다. 이는 다음과 같이 정의됩니다.
struct gl_DepthRangeParameters {
highp float near; // near Z
highp float far; // far Z
highp float diff; // far - near
}
uniform gl_DepthRangeParameters gl_DepthRange;
Built-In Constants
const mediump int gl_MaxVertexAttribs = 16;
const mediump int gl_MaxVertexUniformVectors = 256;
const mediump int gl_MaxVertexOutputVectors = 16;
const mediump int gl_MaxVertexTextureImageUnits = 16;
const mediump int gl_MaxCombinedTextureImageUnits = 32;
- gl_MaxVertexAttribs: 지원가능한 vertex attributes 최대 갯수. ES 3.0에서 보장하는 최소 갯수는 16이다.
- gl_MaxVertexUniformVectors: 지원 가능한 vec4형태의 uniform 최대 갯수. ES 3.0에서 보장하는 최소 갯수는 256개이다. 이때 구현체에 따라서 상수도 uniform으로 치환할 수 있고 초월함수를 사용할 시 구현체 특정 uniform이 사용될 수 있다. 그래서 사실 vertex shader에서 사용가능한 uniform의 정확한 갯수를 알아내는것은 불가능하다. 그래서 (만약 uniform 갯수를 초과했을시) 컴파일을 하고 실패했을때 compile log에서 지금 사용되고 있는 uniform갯수에 대한 정보가 담겨져 있을 수 있다. 하지만 이 역시 구현체마다 다르므로, 그냥 최대한 vertex uniform entries를 아껴서 사용하는 법을 알려 주겠다.
- gl_MaxVertexOutputVectors: Vertex shader의 최대 지원 가능한 output갯수(vec4형태). 최소 보장 갯수는 16개이다.
- gl_MaxVertexTextureImageUnits: Vertex shader의 최대 지원 가능한 texture unit들의 갯수. 최소 보장 갯수는 16개이다.
- gl_MaxCombinedTextureImageUnits: Vertex shader + fragment shader의 최대 지원 가능한 texture unit들의 갯수. 최소 보장 갯수는 32개이다.
Ch05 에서 다룬 정밀도 한정자에 대해 간략히 살펴봅니다.
정밀도 한정자는 부동 소수점 또는 정수 기반 변수의 정밀도를 지정하는데 사용 할 수 있습니다.
정밀도를 지정하는 키워드는 lowp
, meadiump
, highp
입니다.
별도의 지정 없이 기본 정밀도를 사용할 수도 있습니다.
기본 정밀도 한정자는 다음 구문을 통해 각 Shader 의 상단에 지정 할 수 있습니다.
PRECISION HIGHP FLOAT;
PRECISION MEDIUMP INT;
gl_MaxVertexUniformVectors
는 Vertex Shader 에서 사용할 수 있는 최대 유니폼 수를 설명합니다.
OpenGL ES 3.0 에서 지원해야하는 최소값은 256 개의 vec4 엔트리입니다.
유니폼은 다음 변수를 저장하는데 사용됩니다.
- uniform qualifier 로 선언된 변수
- 상수
- 리터럴 값
- Implementation-specific constants
vertex shader안에 정의된 uniforms, const 키워드로 정의된 상수들, literal 값들, 구현체 전용 상수값들은 반드시 gl_MaxVertexUniformVectors값에 맞추어 져야합니다(fit). 만약 맞추어 지지 않는다면 compile은 실패될것입니다. 개발자는 packing rule을 사용해서 uniform variables, 상수들, 리터럴 값들을 저장하는데 총 얼마만큼의 저장공간이 필요할 것인지 계산할 수 있을것입니다. 하지만 implementation-specific constants는 몇개인지 알 수가 없습니다. 왜냐하면 이 값은 구현체에 따라서 다르며 어떤 내장 shading language function을 사용하는지에 따라서도 달라지기 때문입니다. 보통 내장 초월함수를 사용한다면 implementation-specific constants가 사용됩니다.
리터럴 값에 대해서 예컨대 0.0, 0.0이 정의되어 있다면 0.0이 2번 사용된다고 합니다. 따라서 자주 사용되는 리터럴은 상수(const)로 선언해놓고 해당 상수를 여러번 사용하는게 좋습니다.
정점당 두 텍스처 좌표를 변환하는 Vertex Shader 코드 예제를 살펴보면,
#version 300 es
#define NUM_TEXTURES 2
uniform mat4 tex_matrix[NUM_TEXTURES]; // texture
// matrices
uniform bool enable_tex[NUM_TEXTURES]; // texture
// enables
uniform bool enable_tex_matrix[NUM_TEXTURES]; // texture matrix
// enables
in vec4 a_texcoord0; // available if enable_tex[0] is true
in vec4 a_texcoordl; // available if enable_tex[1] is true
out vec4 v_texcoord[NUM_TEXTURES];
void main()
{
v_texcoord[0] = vec4 ( 0.0, 0.0, 0.0, 1.0 );
// texture 0 이 활성화 된 경우
if ( enable_tex[0] )
{
// 텍스처 행렬 0 이 활성화 되면,
if ( enable_tex_matrix[0] )
v_texcoord[0] = tex_matrix[0] * a_texcoord0;
else
v_texcoord[0] = a_texcoord0;
}
v_texcoord[1] = vec4 ( 0.0, 0.0, 0.0, 1.0 );
// 텍스처 1 일 활성화 되면,
if ( enable_tex[1] )
{
// 텍스처 행렬 1이 확성화 되면,
if ( enable_tex_matrix[1] )
v_texcoord[1] = tex_matrix[1] * a_texcoordl;
else
v_texcoord[1] = a_texcoordl;
}
// gl_Position 을 설정해 유효한 Vertex shader 로 만듭니다.
}
위 코드에서 0, 1, 0.0, 1.0 리터럴 값들은 모두 각각 counting되어 uniform storage에 공간을 많이 차지하게 됩니다. 각 1번만 counting되서 uniform storage안에 들어가려면 다음과 같이 상수값들을 사용하면 됩니다.
#version 300 es
#define NUM_TEXTURES 2
const int c_zero = 0;
const int c_one = 1;
uniform mat4 tex_matrix[NUM_TEXTURES]; // texture matrices
uniform bool enable_tex[NUM_TEXTURES]; // texture enables
uniform bool enable_tex_matrix[NUM_TEXTURES]; // texture matrix enables
in vec4 a_texcoord0; // available if enable_tex[0] is true
in vec4 a_texcoordl; // available if enable_tex[1] is true
out vec4 v_texcoord[NUM_TEXTURES];
void main()
{
v_texcoord[c_zero] = vec4(float(c_zero), float(c_zero), float(c_zero), float(c_one));
// is texture 0 enabled
if (enable_tex[c_zero])
{
// is texture matrix 0 enabled
if (enable_tex_matrix[c_zero])
v_texcoord[c_zero] = tex_matrix[c_zero] *a_texcoord0;
else
v_texcoord[c_zero] = a_texcoord0;
}
v_texcoord[c_one] = vec4(float(c_zero), float(c_zero), float(c_zero), float(c_one));
// is texture 1 enabled
if (enable_tex[c_one])
{
// is texture matrix 1 enabled
if (enable_tex_matrix[c_one])
v_texcoord[c_one] = tex_matrix[c_one] *a_texcoordl;
else
v_texcoord[c_one] = a_texcoordl;
}
// set gl_Position to make this into a valid vertex shader
}
이 장은 OpenGL ES 3.0 Shading Language의 한계를 이해하는데 도움을 주었을것입니다. 또한 대부분의 OpenGL ES 3.0 구현체에서 컴파일되고 실행되는 vertex shader를 작성하는 법을 이해하는데 도움을 주었을것입니다.
다음 기능들을 어떻게 vertex shader에서 구현할지, 예제들을 제공하겠습니다.
- Vertex위치를 matrix로 변경하기
- Per-vertex의 diffuse, specular 빛 계산
- Texture coordinate 생성
- Vertex skinning
- texture lookup value와 함께 vertex 위치 변경하기
#version 300 es
// uniforms used by the vertex shader
uniform mat4 u_mvpMatrix; // matrix to convert position from
// model space to clip space
// attribute inputs to the vertex shader
layout(location = 0) in vec4 a_position; // input position value
layout(location = 1) in vec4 a_color; // input color
// vertex shader output, input to the fragment shader
out vec4 v_color;
void main()
{
v_color = a_color;
gl_Position = u_mvpMatrix * a_position;
}
위의 예제는 potision과 그것과 연관된 color data를 attributes로 받습니다. 그리고 position을 4x4 matrix로 변환시키고, output으로 변환된 위치와 color를 내놓습니다.
transformed vertex positions와 primitive time은 setup과정과 primitive를 fragments로 레스터화하는 rasterization stage을 거칩니다. 각 fragment들 마다, 보간된 v_color 값들이 계산되어질것이고 fragment shader에 input으로 들어갈 것입니다.
또한 위 예제에서 model-view-projection(MVP) matrix의 개념이 uniform u_mvpMatrix에 의하여 소개됩니다. Chapter7장의 Coordinate Systems에서 소개되었듯, vertex shaer에 input으로 들어간 위치 data들은 object coordinates를 따릅니다. 그리고 vertex shader의 output으로 나온 위치 data들은 clip coordinates를 따릅니다. MVP matrix는 3가지의 아주 중요한 변환행렬(transformation matrices)의 곱으로 이루어진 하나의 matrix입니다. 각 변환행렬들은 다음 변환을 행합니다: model matrix, view matrix, projection matrix.
- Model matrix — object coordinates → world coordinates 변환.
- View matrix — world coordinates → eye coordinates 변환.
- Projection matrix — eye coordinates → clip coordinates 변환.
전통적인 fixed-function 타입의 OpenGL에서는 model, view matrix들은 합쳐져서 model-view matrix라 불리우는 하나의 matrix로 존재했습니다. 이 4x4 matrix 변환은 object coordinates를 바로 eye coordinates로 변환시킵니다. 이것은 object에서 world coordinates로 변환하는 행렬과 world에서 eye coordinates로 변환하는 행렬의 합쳐진 버젼입니다. fixed-function 타입의 OpenGL에서는, model-view matrix는 glRotatef, glTranslatef, glScalef등과 같은 함수들을 사용해서 생성할 수 있었습니다. 이러한 함수들은 OpenGL ES 2.0, OpenGL ES 3.0 버젼에서는 존재하지 않기 때문에 해당 model-view matrix는 application에 의해 생성하는데 달려있습니다.
이 process를 간소화 하기 위해 우리는 예시 코드 framework를 esTransform.c에 정의해 두었습니다. 이것은 fixed-function OpenGL에서 model-view matrix를 만들기 위한 루틴에서 사용되는 함수들과 동일한 기능들을 제공합니다(esRotate, esTranslate, esScale, esMatrixLoadIdentity, esMatrixMultiply). 이 함수들은 Appendix C에 자세히 설명되어 있습니다. 위의 예시에서 model-view matrix는 다음과 같이 계산되어 집니다:
ESMatrix modelview;
// Generate a model-view matrix to rotate/translate the cube
esMatrixLoadIdentity(&modelview);
// Translate away from the viewer
esTranslate(&modelview, 0.0, 0.0, -2.0);
// Rotate the cube
esRotate(&modelview, userData->angle, 1.0, 0.0, 1.0);
먼저, esMatrixLoadIdentity함수를 사용하여 modelview matrix에 항등행렬이 설정 됩니다. 그리고 이 항등행렬은 object를 viewer로부터 멀리 떨어지게 하는 translation과 결합되어 집니다. 마지막으로, vector(1.0, 0.0, 1.0)을 기준으로 회전하는 rotation이 결합되어집니다. 이때 userData→angle degree 각도만큼 update되어집니다.
Projection Matrix는 chapter7에서 묘사되어진것 처럼, eye coordinates(model-view matrix에 의해서 도출된)를 clip coordinates로 변환시킵니다. Fixed-function OpenGL에서는 이 변환이 glFrustum이나 gluPerspective에 의해서 명시가 되어졌습니다. OpenGL ES Framework API에서, 우리는 2개의 동일한 함수들을 제공합니다: esFrustum, esPerspective. 이 함수들은 Chapter7에 나와있는 clip volume들을 정의합니다. esFrustum은 각 좌표를 직접 정의함으로써 clip volume을 정의합니다. esPerspective함수는 clip volume의 field-of-view와 aspect ratio를 활용해 esFrustum의 parameter들을 알아서 계산해주는 편리한 함수 입니다. 다음은 projection matrix를 계산하는 예시입니다:
ESMatrix projection;
// Compute the window aspect ratio
aspect = (GLfloat) esContext->width /
(GLfloat) esContext->height;
// Generate a perspective matrix with a 60-degree FOV
// and near and far clip planes at 1.0 and 20.0
esMatrixLoadIdentity ( &projection);
esPerspective ( &projection, 60.0f, aspect, 1.0f, 20.0f );
마지막으로 MVP matrix는 model-view, projection matrix의 곱으로 계산되어집니다.
// Compute the final MVP by multiplying the
// model-view and projection matrices together
esMatrixMultiply(&userData->mvpMatrix, &modelview, &projection);
그리고 glUniformMatrix4fv에 의해 uniform으로 로딩되어집니다.
// Get the uniform locations
userData->mvpLoc =
glGetUniformLocation(userData->programObject,
"u_mvpMatrix");
…
// Load the MVP matrix
glUniformMatrix4fv(userData->mvpLoc, 1, GL_FALSE,
(GLfloat*) &userData->mvpMatrix.m[0][0]);
이 장에서는 directional lights, point lights(spotlights)에 대한 빛 방정식을 계산하는 예시들을 볼 것입니다. 이 장에서 묘사된 vertex shader들은 OpenGL ES 1.1의 directional, spot(point) 빛 방정식 모델을 사용합니다. 여기에서 묘사된 빛 예시들은, viewer가 무한대의 위치에 있다고 가정합니다.
directional light는 scene안에서 밝혀지고 있는 object로 부터 무한대의 위치에 존재하는 빛입니다. directional light의 예시는 태양입니다. 빛이 무한대의 거리에 위치함으로써, light source로부터의 빛의 방향은 일정합니다. 따라서 Light direction vector는 상수이며 per vertex마다 계산되어질 필요가 없습니다. 다음 그림은 directional light의 빛 방정식 계산을 위해 필요한 용어들을 묘사하는 그림입니다.
Peye는 viewer의 위치이고, Plight는 light의 position입니다(Plight . w = 0). N은 normal, H는 half-plane vector입니다. Plight.w=0이기 때문에, light direction vector는 Plight.xyz일 것 입니다. Half-plane vector H는 ||VPlight + VPeye|| 입니다. light source와 viewer는 무한의 위치에 있으므로, half-plane 벡터 H는 ||Plight.xyz + (0,0,1)|| 입니다.
8-2예시코드는 directional light를 계산하는 vertex shader code를 제공합니다. directional light properties는 directional_light struct에 정의되어 있습니다. 다음 요소들을 포함합니다:
- direction: eye space에서의 정규화된 light direction
- halfplane: 정규화된 half-plane 백터 H. directional light를 위해 미리 계산되어질 수 있습니다(directional light 위치기 변하지 않으므로).
- ambient_color: light의 ambient color
- diffuse_color: light의 diffuse color
- specular_color: light의 specular color
vertex diffuse와 specular color의 계산을 위해 필요한 물체 특성(material properties)는 material_properties struct에 정의되어 있습니다. 다음 요소들을 포함합니다:
- ambient_color: 물체의 ambient color
- diffuse_color: 물체의 diffuse color
- specular_color: 물체의 specular color
- specular_exponent: material의 shiness를 나타내는 요소. specular highlight의 shininess를 조절할 때 사용된다.
Example 8-2
#version 300 es
struct directional_light
{
vec3 direction; // normalized light direction in eye
// space
vec3 halfplane; // normalized half-plane vector
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
};
struct material_properties
{
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
float specular_exponent;
};
const float c_zero = 0.0;
const float c_one = 1.0;
uniform material_properties material;
uniform directional_light light;
// normal has been transformed into eye space and is a
// normalized vector; this function returns the computed color
vec4 directional_light_color(vec3 normal)
{
vec4 computed_color = vec4(c_zero, c_zero, c_zero,
c_zero);
float ndotl; // dot product of normal &light direction
float ndoth; // dot product of normal &half-plane vector
ndotl = max(c_zero, dot(normal, light.direction));
ndoth = max(c_zero, dot(normal, light.halfplane));
computed_color += (light.ambient_color *
material.ambient_color);
computed_color += (ndotl *light.diffuse_color *
material.diffuse_color);
if (ndoth > c_zero)
{
computed_color += (pow(ndoth,
material.specular_exponent) *
material.specular_color *
light.specular_color);
}
return computed_color;
}
// add a main function to make this into a valid vertex shader
8-2예시에서 묘사된 directional light vertex shader code는 per-vertex의 diffuse, specular color를 결합하여 하나의 color(computed_color)로 합칩니다. 다른 옵션은 각각을 별개의 output variable로 정의해서 fragment shader에 각각 보내주는 방법도 있습니다.
Note: 8-2 예시에서, 우리는 material colors(ambient, diffuse, specular)를 light colors와 곱해주었습니다. 이것은 우리가 오직 하나의 light에서 계산할 시에는 문제가 없습니다. 만약 우리가 다수의 light에 대해 빛 방정식을 계산해야 한다면, 우리는 ambient, diffuse, specular 값들을 각 light에 대해서 계산해야되고, final vertex color를 matrial의 ambient, diffuse, specular 칼라값들을 계산된 항(ambient, diffuse, specular)들과 곱한 뒤, 그것들을 모두 더해줌으로써 계산되어집니다.
Point light는 특정 위치에서 빛을 모든 방향으로 발산하는 빛입니다. point light는 위치 백터(x, y, z, w) w ≠ 0 에 의해 정의되어 집니다. Point light는 모든 방향에 대해서 동일하게 빛을 내뿜지만, 그 강도는 거리(빛과 물체의 거리)에 의해 감쇄되어 집니다. 이 감쇄효과는 다음 방정식으로 부터 계산되어 집니다:
K0, K1, 그리고 K2는 각각 constant, linear, 2차식 감쇄효과 factor들입니다.
spotlight(point light)는 위치와 방향이 요소가 있는데, 이 두가지는 Plight에서 방출되어 spotdirection방향인 원뿔형태의 빛을 시뮬레이팅합니다.
다음 Figure 8-4는 spotlight를 계산하는 lighting equation에 필요한 용어들을 묘사하고 있습니다.
방출된 빛의 강도는 spot cutoff 계수에 의해서도 감쇄되어집니다. 이 spot cutoff 계수는 원뿔의 정중앙을 가로지르는 벡터로부터 얼마나 (각도가 벌어진 채) 떨어져 있는가를 나타냅니다. 이 원뿔의 정중앙으로부터 얼마나 떨어져있는지는 VPlight와 spotdirection의 dot product으로 계산되어 집니다. Spot cutoff 계수는 다음과 같이 주어진 스포트라이트 방향에서 1.0입니다. 그리고 만약 그 각도가 spotcutoff angle을 벗어난다면, 0.0까지 기하급수적으로 줄어듭니다.
Example 8-3는 spot(point) light의 lighting equation을 계산하는 vertex shader code를 묘사합니다. spotlight 속성들은 spot_light struct 구조체에 정의되어 있습니다. 다음 요소들을 포함합니다:
- direction - eye space 에서의 light direction
- ambient_color — 빛의 ambient color
- diffuse_color — 빛의 diffuse color
- specular_color — 빛의 specular color
- attenuation_factors — 감쇄 계수: K0, k1, k2
- compute_distance_attenuation — 거리 감쇄효과를 enable할 것인지를 나타내는 booloan 값.
- spot_direction — spot cutoff factgor를 계산할 때 사용되는 spotlight exponent.
- spot_cutoff_angle — spotlight cutoff angle.
#version 300 es
struct spot_light
{
vec4 position; // light position in eye space
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
vec3 spot_direction; // normalized spot direction
vec3 attenuation_factors; // attenuation factors K0, K1, K2
bool compute_distance_attenuation;
float spot_exponent; // spotlight exponent term
float spot_cutoff_angle; // spot cutoff angle in degrees
};
struct material_properties
{
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
float specular_exponent;
};
const float c_zero = 0.0;
const float c_one = 1.0;
uniform material_properties material;
uniform spot_light light;
// normal and position are normal and position values in
// eye space.
// normal is a normalized vector.
// This function returns the computed color.
vec4 spot_light_color(vec3 normal, vec4 position)
{
vec4 computed_color = vec4(c_zero, c_zero, c_zero,
c_zero);
vec3 lightdir;
vec3 halfplane;
float ndotl, ndoth;
float att_factor;
att_factor = c_one;
// we assume "w" values for light position and
// vertex position are the same
lightdir = light.position.xyz - position.xyz;
// compute distance attenuation
if (light.compute_distance_attenuation)
{
vec3 att_dist;
att_dist.x = c_one;
att_dist.z = dot(lightdir, lightdir);
att_dist.y = sqrt(att_dist.z);
att_factor = c_one / dot(att_dist,
light.attenuation_factors);
}
// normalize the light direction vector
lightdir = normalize(lightdir);
// compute spot cutoff factor
if (light.spot_cutoff_angle < 180.0)
{
float spot_factor = dot(-lightdir,
light.spot_direction);
if (spot_factor >= cos(radians(light.spot_cutoff_angle)))
spot_factor = pow(spot_factor, light.spot_exponent);
else
spot_factor = c_zero;
// compute combined distance and spot attenuation factor
att_factor *= spot_factor;
}
if (att_factor > c_zero)
{
// process lighting equation --> compute the light color
computed_color += (light.ambient_color *
material.ambient_color);
ndotl = max(c_zero, dot(normal, lightdir));
computed_color += (ndotl *light.diffuse_color *
material.diffuse_color);
halfplane = normalize(lightdir + vec3(c_zero, c_zero,
c_one));
ndoth = dot(normal, halfplane);
if (ndoth > c_zero)
{
computed_color += (pow(ndoth,
material.specular_exponent) *
material.specular_color *
light.specular_color);
}
// multiply color with computed attenuation
computed_color *= att_factor;
}
return computed_color;
}
// add a main function to make this into a valid vertex shader
우리는 vertex shader안에서 texture coordinates를 생성하는 두개의 예시를 볼 것입니다. 이 두가지 예시는 scene안에서 reflection vector를 생성한 후 이것을 위도 경도 map(sphere map이라고도 불림)이나 cubemap으로 인덱싱 되어지는 texture coordinate를 계산할때 사용하여, 반짝이는(즉 반사되는) 물체를 랜더링 할때 사용된다. fixed-function OpenGL 명세는 GL_SPHERE_MAP, GL_REFLECTION_MAP으로 각각 texture coordinate generation mode들을 제어하도록 표기되어 있다. GL_SPHERE_MAP 모드는 reflection vector를 사용하여 2D texture map을 조회하기 위한 2D texture coordinate를 계산하는 텍스처 좌표를 생성합니다. GL_REFLECTION_MAP 모드는 reflection vector인 texture coordinate를 생성하고, 그것을 cubemap을 조회하기 위한 3D texture coordinate로써 사용할 수 있습니다.
Example 8-4: Sphere Map 텍스쳐 좌표 생성
// position is the normalized position coordinate in eye space.
// normal is the normalized normal coordinate in eye space.
// This function returns a vec2 texture coordinate.
vec2 sphere_map(vec3 position, vec3 normal)
{
reflection = reflect(position, normal);
m = 2.0* sqrt(reflection.x *reflection.x +
reflection.y *reflection.y +
(reflection.z + 1.0) *(reflection.z + 1.0));
return vec2((reflection.x / m + 0.5),
(reflection.y / m + 0.5));
}
Example 8-5: Cube Map 텍스쳐 좌표 생성
// position is the normalized position coordinate in eye space.
// normal is the normalized normal coordinate in eye space.
// This function returns the reflection vector as a vec3 texture
// coordinate.
vec3 cube_map ( vec3 position, vec3 normal )
{
return reflect ( position, normal );
}
정점 스키닝(Vertex Skinning)은 다각형 사이의 결합을 부드럽게 만드는 데 일반적으로 사용되는 기술입니다. 이는 각 정점에 적절한 가중치를 가진 추가 변환 행렬을 적용하여 구현됩니다. 정점 스킨에 사용되는 여러 행렬은 행렬 팔레트에 저장됩니다. 정점당 행렬의 인덱스는 정점을 스킨하는 데 사용될 행렬 팔레트의 적절한 행렬을 참조하는 데 사용됩니다. 정점 스키닝은 일반적으로 3D 게임의 캐릭터 모델에 사용되어 추가 형상을 사용하지 않고도 캐릭터 모델이 최대한 부드럽고 사실적으로 보이도록 합니다. 정점을 스킨 처리하는 데 사용되는 행렬 수는 일반적으로 2~4개입니다.
정점 스키닝의 수학은 다음 방정식으로 제공됩니다.
n 정점을 변환하는 데 사용될 행렬의 수입니다.
P 정점의 위치 입니다.
P' 변환된(스킨 처리된) 위치입니다.
N 정점의 노말입니다.
N' 변환된(스킨 처리된) 노말입니다.
Mi 는 정점당 i번째 행렬과 연관된 행렬이며 다음과 같이 계산됩니다.
Mi = matrix_palette [ matrix_index[i] ] with n matrix_index values specified per vertex
Mi–1T 행렬 Mi의 역전치행렬입니다. Wi 행렬과 관련된 가중치입니다.
32개 행렬의 행렬 팔레트와 정점당 최대 4개의 행렬을 사용하여 정점 스키닝을 구현하여 스키닝된 정점을 생성하는 방법을 논의합니다. 32개 행렬의 행렬 팔레트 크기는 매우 일반적입니다. 행렬 팔레트의 행렬은 일반적으로 4 × 3 열 주요 행렬입니다(즉, 행렬당 4개의 vec3 항목). 행렬을 열 주요 순서로 저장하려면 행을 저장하는 데 각 균일 항목의 3개 요소가 포함된 128개의 균일 항목이 필요합니다. 모든 OpenGL ES 3.0 구현에서 지원되는 gl_MaxVertexUniformVectors
의 최소값은 256 vec4 항목입니다. 따라서 우리는 256개의 vec4 유니폼 항목 중 네 번째 행만 사용할 수 있습니다. 부동 소수점 값의 이 행은 (균일 패킹 규칙에 따라) float 유형으로 선언된 유니폼만 저장할 수 있습니다. 따라서 vec2, vec3 또는 vec4 유니폼을 저장할 공간이 없습니다. 행렬당 3개의 vec4 항목을 사용하여 행 우선 순서로 팔레트에 행렬을 저장하는 것이 더 좋습니다. 이렇게 하면 유니폼 저장에 96개의 vec4 항목을 사용하고 나머지 160개의 vec4 항목은 다른 유니폼을 저장하는 데 사용할 수 있습니다. 스킨 노멀을 계산하는 데 필요한 역전위 행렬을 저장할 균일한 저장소가 충분하지 않습니다. 그러나 이는 일반적으로 문제가 되지 않습니다. 대부분의 경우 사용되는 행렬은 정규직교이므로 정점 위치와 법선을 변환하는 데 사용할 수 있습니다. 예제 8-6에서는 스킨 노멀과 위치를 계산하는 정점 셰이더 코드를 보여줍니다. 행렬 팔레트에는 32개의 행렬이 포함되어 있고 이러한 행렬은 행 우선 순서로 저장되어 있다고 가정합니다. 행렬은 또한 정규직교(즉, 위치와 법선을 변환하는 데 동일한 행렬을 사용할 수 있음)로 가정되며 각 정점을 변환하는 데 최대 4개의 행렬이 사용됩니다.
예제 8-6
#version 300 es
#define NUM_MATRICES 32 // 32 matrices in matrix palette
const int c_zero = 0;
const int c_one = 1;
const int c_two = 2;
const int c_three = 3;
// store 32 4 x 3 matrices as an array of floats representing
// each matrix in row-major order (i.e., 3 vec4s)
uniform vec4 matrix_palette[NUM_MATRICES * 3];
// vertex position and normal attributes
in vec4 a_position;
in vec3 a_normal;
// matrix weights - 4 entries / vertex
in vec4 a_matrixweights;
// matrix palette indices
in vec4 a_matrixindices;
void skin_position ( in vec4 position, float m_wt, int m_indx,
out vec4 skinned_position )
{
vec4 tmp;
tmp.x = dot ( position, matrix_palette[m_indx] );
tmp.y = dot ( position, matrix_palette[m_indx + c_one] );
tmp.z = dot ( position, matrix_palette[m_indx + c_two] );
tmp.w = position.w;
skinned_position += m_wt * tmp;
}
void skin_normal ( in vec3 normal, float m_wt, int m_indx,
inout vec3 skinned_normal )
{
vec3 tmp;
tmp.x = dot ( normal, matrix_palette[m_indx].xyz );
tmp.y = dot ( normal, matrix_palette[m_indx + c_one].xyz );
tmp.z = dot ( normal, matrix_palette[m_indx + c_two].xyz );
skinned_normal += m_wt * tmp;
}
void do_skinning ( in vec4 position, in vec3 normal,
out vec4 skinned_position,
out vec3 skinned_normal )
{
skinned_position = vec4 ( float ( c_zero ) );
skinned_normal = vec3 ( float ( c_zero ) );
// transform position and normal to eye space using matrix
// palette with four matrices used to transform a vertex
float m_wt = a_matrixweights[0];
int m_indx = int ( a_matrixindices[0] ) * c_three;
skin_position ( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
m_wt = a_matrixweights[1] ;
m_indx = int ( a_matrixindices[1] ) * c_three;
skin_position ( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
m_wt = a_matrixweights[2];
m_indx = int ( a_matrixindices[2] ) * c_three;
skin_position ( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
m_wt = a_matrixweights[3];
m_indx = int ( a_matrixindices[3] ) * c_three;
skin_position ( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
}
// add a main function to make this into a valid vertex shader
예제 8-6에서 정점 스키닝 셰이더는 4개의 행렬과 적절한 행렬 가중치를 사용하여 정점을 변환하여 스킨된 정점을 생성합니다. 이러한 행렬 가중치 중 일부가 0일 수도 있고 매우 일반적입니다. 예제 8-6에서는 가중치에 관계없이 4개의 행렬을 모두 사용하여 정점이 변환됩니다. 그러나 skin_position
및 skin_normal
을 호출하기 전에 조건식을 사용하여 행렬 가중치가 0인지 확인하는 것이 더 나을 수도 있습니다. 예제 8-7에서 정점 스키닝 셰이더는 행렬 변환을 적용하기 전에 행렬 가중치가 0인지 확인합니다.
예제 8-7
void do_skinning ( in vec4 position, in vec3 normal,
out vec4 skinned_position,
out vec3 skinned_normal )
{
skinned_position = vec4 ( float ( c_zero ) );
skinned_normal = vec3 ( float( c_zero ) );
// transform position and normal to eye space using matrix
// palette with four matrices used to transform a vertex
int m_indx = 0;
float m_wt = a_matrixweights[0];
if ( m_wt > 0.0 )
{
m_indx = int ( a_matrixindices[0] ) * c_three;
skin_position( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
}
m_wt = a_matrixweights[1] ;
if ( m_wt > 0.0 )
{
m_indx = int ( a_matrixindices[1] ) * c_three;
skin_position( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
}
m_wt = a_matrixweights[2] ;
if ( m_wt > 0.0 )
{
m_indx = int ( a_matrixindices[2] ) * c_three;
skin_position( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
}
m_wt = a_matrixweights[3];
if ( m_wt > 0.0 )
{
m_indx = int ( a_matrixindices[3] ) * c_three;
skin_position( position, m_wt, m_indx, skinned_position );
skin_normal ( normal, m_wt, m_indx, skinned_normal );
} }
언뜻 보면 예제 8-7의 정점 스키닝 셰이더가 예제 8-6의 정점 스키닝 셰이더보다 더 나은 성능을 제공한다는 결론을 내릴 수 있습니다. 이것이 반드시 사실은 아닙니다. 실제로 대답은 GPU에 따라 다를 수 있습니다. 이러한 변화는 조건식 if (m_wt > 0.0)에서 m_wt가 동적 값이고 GPU에 의해 병렬로 실행되는 정점에 대해 다를 수 있기 때문에 발생합니다. 이제 병렬로 실행되는 정점이 m_wt에 대해 서로 다른 값을 가질 수 있으며 이로 인해 실행이 직렬화될 수 있는 발산 흐름 제어가 실행됩니다. GPU가 발산 흐름 제어를 효율적으로 구현하지 않는 경우 예제 8-7의 정점 셰이더는 예제 8 6의 버전만큼 효율적이지 않을 수 있습니다. 따라서 애플리케이션은 GPU에서 테스트 셰이더를 실행하여 발산 흐름 제어의 성능을 테스트해야 합니다. 사용할 셰이더를 결정하기 위한 애플리케이션 초기화 단계의 일부인 GPU입니다.
transform feedback 모드를 사용하면 정점 셰이더의 출력을 버퍼 개체로 캡처할 수 있습니다. 그런 다음 출력 버퍼는 후속 그리기 호출에서 정점 데이터의 소스로 사용될 수 있습니다. 이 접근 방식은 CPU 개입 없이 GPU에서 애니메이션을 수행하는 광범위한 기술(예: 정점 버퍼 렌더링을 사용하는 particle 애니메이션 또는 물리 시뮬레이션)에 유용합니다.
변환 피드백 모드 중에 캡처할 정점 속성 세트를 지정하려면 다음 명령을 사용하십시오.
void glTransformFeedbackVaryings(GLuint program,
GLsizei count,
const char** varyings,
GLenum bufferMode)
program specifies the handle to the program object.
count specifies the number of vertex output variables used for transform feedback.
varyings. specifies an array of count zero-terminated strings specifying
the names of the vertex output variables to use for transform feedback.
bufferMode specifies the mode used to capture the vertex output variables when transform feedback is active.
Valid values are GL_INTERLEAVED_ATTRIBS, to capture
the vertex output variables into a single buffer,
and GL_SEPARATE_ATTRIBS, to capture each vertex output variable into its own buffer.
glTransformFeedbackVaryings
를 호출한 후 glLinkProgram
을 사용하여 프로그램 개체를 연결해야 합니다. 예를 들어 하나의 변환 피드백 버퍼에 캡처할 두 개의 정점 속성을 지정하려면 코드는 다음과 같습니다.
const char* varyings[] = { "v_position", "v_color" };
glTransformFeedbackVarying ( programObject, 2, varyings,
GL_INTERLEAVED_ATTRIBS );
glLinkProgram ( programObject );
그런 다음 GL_TRANSFORM_FEED-BACK_BUFFER
와 함께 glBindBuffer
를 사용하여 하나 이상의 버퍼 객체를 변환 피드백 버퍼로 바인딩해야 합니다. 버퍼는 GL_TRANSFORM_FEEDBACK_BUFFER
와 함께 glBufferData
를 사용하여 할당되고 glBindBufferBase
또는 glBindBufferRange
를 사용하여 인덱싱된 바인딩 지점에 바인딩됩니다. 이러한 버퍼 API에 대한 자세한 내용은 6장 "Vertex Attributes, Vertex Arrays, and Buffer Objects"에 설명되어 있습니다.
변환 피드백 버퍼가 바인딩된 후 다음 API 호출을 사용하여 변환 피드백 모드를 시작하고 종료할 수 있습니다.
void glBeginTransformFeedback(GLenum primitiveMode)
void glEndTransformFeedback()
primitiveMode specifies the output type of the primitives
that will be captured into the buffer objects
that are bound for transform feedback.
Transform feedback is limited to non-indexed
GL_POINTS, GL_LINES, and GL_TRIANGLES.
glBeginTransformFeedback
과 glEndTransformFeedback
사이에 발생하는 모든 그리기 호출은 정점 출력을 변환 피드백 버퍼에 캡처합니다. 표 8-1은 transform feedback primitive mode 에 해당하는 허용된 그리기 모드를 나타냅니다.
GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN
으로 glBeginQuery
및 glEndQuery
를 설정한 후 glGetQueryObjectuiv
를 사용하여 변환 버퍼 개체에 성공적으로 작성된 기본 요소 수를 검색할 수 있습니다. 예를 들어, 포인트 세트를 렌더링하고 작성된 포인트 수를 쿼리하기 위한 변환 피드백 모드를 시작하고 종료하려면 코드는 다음과 같습니다.
glBeginTransformFeedback ( GL_POINTS );
glBeginQuery ( GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN,
queryObject );
glDrawArrays ( GL_POINTS, 0, 10 );
glEndQuery ( GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN );
glEndTransformFeedback ( );
// query the number of primitives written
glGetQueryObjectuiv( queryObject, GL_QUERY_RESULT,
&numPoints );
GL_RASTERIZER_DISCARD
와 함께 glEnable
및 glDisable
을 사용하여 transform feedback 모드에서 캡처하는 동안 래스터화를 비활성화하거나 활성화할 수 있습니다. GL_RASTERIZER_DISCARD
가 활성화되어 있는 동안에는 프래그먼트 셰이더가 실행되지 않습니다. 14장 "Advanced Programming with OpenGL ES3.0"의 transform feedback을 사용하는 입자 시스템 예제에서 변환 피드백을 사용하는 전체 예제를 설명합니다.
OpenGL ES 3.0은 정점 셰이더에서 텍스처 lookup 작업을 지원합니다. 이는 정점 셰이더의 텍스처 조회 값을 기반으로 정점 법선을 따라 정점 위치를 displace 할 수 있는 displacement mapping과 같은 기술을 구현하는 데 유용합니다. displacement mapping 기술의 일반적인 적용은 지형이나 수면을 렌더링하는 것입니다.
정점 셰이더에서 텍스처 lookup을 수행하는 데에는 몇 가지 주목할만한 제한 사항이 있습니다.
• level of detail은 암시적(implicitly)으로 계산되지 않습니다.
cf) 암시적이라는 것은 x^2 + y^2 = r^2 처럼 음함수를 통해 범위를 표현하는 것을 뜻한다. • 텍스처 lookup function의 바이어스 매개변수는 허용되지 않습니다. • 기본 텍스처는 밉매핑된 텍스처에 사용됩니다.
cf) mipmap 렌더링 속도를 향상시키기 위한 목적으로 기본 텍스처와 이를 연속적으로 미리 축소시킨 텍스처들로 이루어진 비트맵 이미지의 집합이다.
구현에서 지원하는 최대 텍스처 이미지 단위 수는 GL_MAX_VERTEX_TEXTURE_UNITS
와 함께 glGetIntegerv
를 사용하여 쿼리할 수 있습니다. OpenGL ES 3.0 구현이 지원할 수 있는 최소 숫자는 16입니다. 예제 8-8은 변위 매핑을 수행하는 샘플 정점 셰이더입니다. 다양한 텍스처 단위에 텍스처를 로드하는 프로세스는 9장 "Texturing"에 자세히 설명되어 있습니다.
예제 8-8
#version 300 es
// uniforms used by the vertex shader
uniform mat4 u_mvpMatrix; // matrix to convert P from
// model space to clip space
uniform sampler2D displacementMap;
// attribute inputs to the vertex shader
layout(location = 0) in vec4 a_position; // input position value
layout(location = 1) in vec3 a_normal; // input normal value
layout(location = 2) in vec2 a_texcoord; // input texcoord value
layout(location = 3) in vec4 a_color; // input color
// vertex shader output, input to the fragment shader
out vec4 v_color;
void main ( ) {
v_color = a_color;
float displacement = texture ( displacementMap,
a_texcoord ).a;
vec4 displaced_position = a_position +
vec4 ( a_normal * displacement, 0.0 );
gl_Position = u_mvpMatrix * displaced_position;
}
이제 정점 스키닝 없이 OpenGL ES 1.1 고정 기능 정점 파이프라인을 구현하는 정점 셰이더에 대해 논의합니다. 이는 또한 정점 셰이더가 얼마나 큰지 파악하고 모든 OpenGL ES 3.0 구현에서 계속 실행될 수 있는지 알아내는 흥미로운 연습이 될 것입니다. 이 정점 셰이더는 OpenGL ES 1.1 정점 파이프라인의 다음과 같은 고정 기능을 구현합니다.
• 필요한 경우 법선과 위치를 eye space으로 변환합니다(일반적으로 조명에 필요함). 법선의 크기 조정이나 정규화도 수행됩니다. • 최대 8개의 방향 조명, 점 조명 또는 정점당 양면 조명 및 색상 재질이 있는 스포트라이트에 대한 OpenGL ES 1.1 정점 조명 방정식을 계산합니다. • 정점당 최대 2개의 텍스처 좌표에 대한 텍스처 좌표를 변환합니다.
• 프래그먼트 셰이더에 전달된 안개 요소를 계산합니다. 프래그먼트 셰이더는 안개 요소를 사용하여 안개 색상과 정점 색상 사이를 보간합니다.
• 정점별 사용자 클립 평면 요소를 계산합니다. 하나의 사용자 클립 평면만 지원됩니다. • 위치를 클립 공간으로 변환합니다.
예제 8-9
#version 300 es
//**************************************************************
//
// OpenGL ES 3.0 vertex shader that implements the following
// OpenGL ES 1.1 fixed-function pipeline
//
// - compute lighting equation for up to eight
// directional/point/spotlights
// - transform position to clip coordinates
// - texture coordinate transforms for up to two texture
// coordinates
// - compute fog factor
// - compute user clip plane dot product (stored as
// v_ucp_factor)
//
//**************************************************************
#define NUM_TEXTURES 2
#define GLI_FOG_MODE_LINEAR 0
#define GLI_FOG_MODE_EXP 1
#define GLI_FOG_MODE_EXP2 2
struct light
{
vec4 position; // light position for a point/spotlight or
// normalized dir. for a directional light
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
vec3 spot_direction;
vec3 attenuation_factors;
float spot_exponent;
float spot_cutoff_angle;
bool compute_distance_attenuation;
};
struct material
{
vec4 ambient_color;
vec4 diffuse_color;
vec4 specular_color;
vec4 emissive_color;
float specular_exponent;
};
const float c_zero = 0.0;
const float c_one = 1.0;
const int indx_zero = 0;
const int indx_one = 1;
uniform mat4 mvp_matrix; // combined model-view +
// projection matrix
uniform mat4 modelview_matrix; // model-view matrix
uniform mat3 inv_transpose_modelview_matrix; // inverse
// model-view matrix used
// to transform normal
uniform mat4 tex_matrix[NUM_TEXTURES]; // texture matrices
uniform bool enable_tex[NUM_TEXTURES]; // texture enables
uniform bool enable_tex_matrix[NUM_TEXTURES]; // texture
// matrix enables
uniform material material_state;
uniform vec4 ambient_scene_color;
uniform light light_state[8];
uniform bool light_enable_state[8]; // booleans to indicate
// which of eight
// lights are enabled
uniform int num_lights; // number of lights
// enabled = sum of
// light_enable_state bools
// set to TRUE
uniform bool enable_lighting; // is lighting enabled
uniform bool light_model_two_sided; // is two-sided
// lighting enabled
uniform bool enable_color_material; // is color material
// enabled
uniform bool enable_fog; // is fog enabled
uniform float fog_density;
uniform float fog_start, fog_end;
uniform int fog_mode; // fog mode: linear, exp, or exp2
uniform bool xform_eye_p; // xform_eye_p is set if we need
// Peye for user clip plane,
// lighting, or fog
uniform bool rescale_normal; // is rescale normal enabled
uniform bool normalize_normal; // is normalize normal
// enabled
uniform float rescale_normal_factor; // rescale normal
// factor if
// glEnable(GL_RESCALE_NORMAL)
uniform vec4 ucp_eqn; // user clip plane equation;
// one user clip plane specified
uniform bool enable_ucp; // is user clip plane enabled
//******************************************************
// vertex attributes: not all of them may be passed in
//******************************************************
in vec4 a_position; // this attribute is always specified
in vec4 a_texcoord0; // available if enable_tex[0] is true
in vec4 a_texcoordl; // available if enable_tex[1] is true
in vec4 a_color; // available if !enable_lighting or
// (enable_lighting && enable_color_material)
in vec3 a_normal; // available if xform_normal is set
// (required for lighting)
//************************************************
// output variables of the vertex shader
//************************************************
out vec4 v_texcoord[NUM_TEXTURES];
out vec4 v_front_color;
out vec4 v_back_color;
out float v_fog_factor;
out float v_ucp_factor;
//************************************************
// temporary variables used by the vertex shader
//************************************************
vec4 p_eye;
vec3 n;
vec4 mat_ambient_color;
vec4 mat_diffuse_color;
vec4 lighting_equation ( int i )
{
vec4 computed_color = vec4( c_zero, c_zero, c_zero,
c_zero );
vec3 h_vec;
float ndotl, ndoth;
float att_factor;
vec3 VPpli;
att_factor = c_one;
if ( light_state[i].position.w != c_zero )
{
float spot_factor;
vec3 att_dist;
// this is a point or spotlight
// we assume "w" values for PPli and V are the same
VPpli = light_state[i].position.xyz - p_eye.xyz;
if ( light_state[i].compute_distance_attenuation )
{
// compute distance attenuation
att_dist.x = c_one;
att_dist.z = dot ( VPpli, VPpli );
att_dist.y = sqrt ( att_dist.z ) ;
att_factor = c_one / dot ( att_dist,
light_state[i] .attenuation_factors );
}
VPpli = normalize ( VPpli );
if ( light_state[i].spot_cutoff_angle < 180.0 )
{
// compute spot factor
spot_factor = dot ( -VPpli,
light_state[i].spot_direction );
if( spot_factor >= cos ( radians (
light_state[i].spot_cutoff_angle ) ) )
spot_factor = pow ( spot_factor,
light_state[i].spot_exponent );else
spot_factor = c_zero;
att_factor *= spot_factor;
}
}
else {
// directional light
VPpli = light_state[i].position.xyz;
}
if( att_factor > c_zero )
{
// process lighting equation --> compute the light color
computed_color += ( light_state[i].ambient_color *
mat_ambient_color );
ndotl = max( c_zero, dot( n, VPpli ) );
computed_color += ( ndotl * light_state[i].diffuse_color *
mat_diffuse_color );
h_vec = normalize( VPpli + vec3(c_zero, c_zero, c_one ) );
ndoth = dot ( n, h_vec );
if ( ndoth > c_zero )
{
computed_color += ( pow ( ndoth,
material_state.specular_exponent ) *
material_state.specular_color *
light_state[i].specular_color );
}
computed_color *= att_factor; // multiply color with}
return computed_color;
}
float compute_fog( )
{
float f;
// use eye Z as approximation
if ( fog_mode == GLI_FOG_MODE_LINEAR )
{
f = ( fog_end - p_eye.z ) / ( fog_end - fog_start );
}
else if ( fog_mode == GLI_FOG_MODE_EXP )
{
f = exp( - ( p_eye.z * fog_density ) );
}
else {
f = ( p_eye.z * fog_density );
f = exp( -( f * f ) );
}
f = clamp ( f, c_zero, c_one) ;
return f; }
vec4 do_lighting( )
{
vec4 vtx_color;
int i, j ;
vtx_color = material_state.emissive_color +
( mat_ambient_color * ambient_scene_color );j = int( c_zero );
for ( i=int( c_zero ); i<8; i++ )
{
if ( j >= num_lights )
break;
if ( light_enable_state[i] )
{
j++;
vtx_color += lighting_equation(i);
}
}
vtx_color.a = mat_diffuse_color.a;
return vtx_color;
}
void main( void )
{
int i, j;
// do we need to transform P
if ( xform_eye_p )
p_eye = modelview_matrix * a_position;
if ( enable_lighting )
{
n = inv_transpose_modelview_matrix * a_normal;
if ( rescale_normal )
n = rescale_normal_factor * n;
if ( normalize_normal )
n = normalize(n);
mat_ambient_color = enable_color_material ? a_color
: material_state.ambient_color;
mat_diffuse_color = enable_color_material ? a_color
: material_state.diffuse_color;
v_front_color = do_lighting( );
v_back_color = v_front_color;
// do two-sided lighting
if ( light_model_two_sided )
{
n = -n;
v_back_color = do_lighting( );
}
} else {
// set the default output color to be the per-vertex /
// per-primitive color
v_front_color = a_color;
v_back_color = a_color;
}
// do texture transforms
v_texcoord[indx_zero] = vec4( c_zero, c_zero, c_zero,
c_one );
if ( enable_tex[indx_zero] )
{
if ( enable_tex_matrix[indx_zero] )
v_texcoord[indx_zero] = tex_matrix[indx_zero] *
a_texcoord0;
else
v_texcoord[indx_zero] = a_texcoord0;
}
v_texcoord[indx_one] = vec4( c_zero, c_zero, c_zero, c_one );
if ( enable_tex[indx_one] )
{
if ( enable_tex_matrix[indx_one] )
v_texcoord[indx_one] = tex_matrix[indx_one] *
a_texcoordl;
else
v_texcoord[indx_one] = a_texcoordl;
}
v_ucp_factor = enable_ucp ? dot ( p_eye, ucp_eqn ) : c_zero;
v_fog_factor = enable_fog ? compute_fog( ) : c_one;
gl_Position = mvp_matrix * a_position;
}
이 장에서는 정점 셰이더가 파이프라인에 어떻게 적용되는지, 그리고 몇 가지 정점 셰이더 예제를 통해 정점 셰이더에서 변환, 조명, 스키닝 및 displacement 매핑을 수행하는 방법에 대한 high-level의 개요를 제공했습니다. 또한 변환 피드백 모드를 사용하여 정점 출력을 버퍼 개체로 캡처하는 방법과 정점 셰이더를 사용하여 고정 기능 파이프라인을 구현하는 방법을 배웠습니다. 다음으로, 프래그먼트 셰이더를 논의하기 전에 OpenGL ES 3.0의 텍스처링 기능을 다루겠습니다.
OpenGL_ES 3.0을 기반으로 하고있습니다 🍡
OpenGLES3.0 Programming Guide 책을 해설 및 번역 작업과 예제 코드 작성을 진행중입니다.
챕터 | 완료여부 |
---|---|
chapter 1 | |
chapter 2 | ✅ |
chapter 3 | ✅ |
chapter 4 | ✅ |
chapter 5 | ✅ |
chapter 6 | ✅ |
chapter 7 | ✅ |
chapter 8 | ✅ |
chapter 9 |