free garden
Chap 3. The Basics of GLSL Shaders 본문
Vertex and fragment shaders
OpenGL 4.3 이상부터는 셰이더에 다음 여섯 가지 단계가 있다. 이번 장에서는 Vertex와 Fragment 단계에 대해서 알아본다. (2 ~ 4는 Chap 7, 6은 Chap 11 에서 다룰 예정이다.)
1. Vertex
2. Geometry
3. Tessellation Control
4. Tessellation Evaluation
5. Fragment
6. Compute
Vertex Data는 Shader Input Variable을 통해 버텍스 셰이더(Vertex Shader)에 도달한다.
Vertex Shader의 Input Variable를 Vertex Attribute라고 한다. 셰이더에서 Input Variable는 앱에서 전달하기도 하고, 이전 단계의 파이프라인(다른 셰이더)에서 전달되기도 한다.
Diffuse and per-vertex shading with a single point light source
확산광(Diffuse): 표면이 방향에 관계 없이 균일하게 주변에 빛을 흩뿌린다.
s: 표변으로부터 빛까지의 방향 벡터
n: 표면의 법선 벡터(Normal Vector)
* 두 단위 벡터(Unit Vector)의 내적은 그들 사이의 cosine 값과 같다.
L: 광원의 세기
K: Diffuse Reflectivity, 반사 계수(Reflection Coefficient)라고도 하며 들어오는 빛에 비해 얼마나 흩어지는 지에 정도를 나타낸다.
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
out vec3 LightIntensity;
uniform vec4 LightPosition; // Light position in camera coords
uniform vec3 Kd;
uniform vec3 Ld;
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP; // Proejction * ModelView
void main()
{
// Convert normal and position to eye coords
vec3 tnorm = normalize(NormalMatrix * VertexNormal);
vec4 camCoords = ModelViewMatrix * vec4(VertexPosition, 1.0);
vec3 s = normalize(vec3(LightPosition - camCoords));
// The diffuse shading equation
LightIntensity = Ld * Kd * max(dot(s, tnorm), 0.0);
// Convert position to clip coordinates and pass along
gl_Position = MVP * vec4(VertexPosition, 1.0);
}
in vec3 LightIntensity;
layout (location = 0) out vec4 FragColor;
void main() {
FragColor = vec4(LightIntensity, 1.0);
}
Implementing the Phong reflection model
환경광(Ambient)
반사광(Specular)
layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
out vec3 LightIntensity;
uniform struct LightInfo {
vec3 Position; // Light position in eye coords
vec3 La; // Ambient light intensity
vec3 Ld; // Diffuse light intensity
vec3 Ls; // Specular light intensity
} LIght;
uniform struct MaterialInfo {
vec3 Ka; // Ambient reflectivity
vec3 Kd; // Diffuse reflectivity
vec3 Ks; // Specular reflectivity
float Shininess; // Specular shininess factor
} Material;
uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;
void main() {
vec3 n = normalize(NormalMatrix * VertexNormal);
vec4 camCoords = ModelViewMatrix * vec4(VertexPosition, 1.0);
vec3 ambient = Light.La * Material.Ka;
vec3 s = normalize(vec3(Light.Position - camCoords));
float sDotN = max(dot(s, n), 0.0);
vec3 diffuse = Light.Ld * Material.Kd * sDotN;
vec3 spec = vec3(0.0);
if (sDotN > 0.0) {
vec3 v = normalize(-camCoords.xyz);
vec3 r = reflect(-s, n);
spec = Light.Ls * Material.Ks * pow(max(dot(r, v), 0.0), Material.Shininess);
}
LightIntensity = ambient + diffuse + spec;
gl_Position = MVP * vec4(VertexPosition, 1.0);
}
Directional lights
한 방향으로 같은 양의 빛을 투사하는 빛. 광원에 대한 position이 없고 오직 direction만 있다.
거리에 따른 빛의 희석(Light attenuation with distance)
vec3 phongModel(vec3 position, vec3 n)
{
vec3 ambient = Light.La * Material.Ka;
vec3 s = normalize(Light.Position.xyz - position);
float sDotN = max(dot(s, n), 0.0);
vec3 diffuse = Light.Ld * Material.Kd * sDotN;
vec3 spec = vec3(0.0);
if (sDotN > 0.0) {
vec3 v = normalize(-position.xyz);
vec3 r = reflect(-s, n);
spec = Light.Ls * Material.Ks * pow(max(dot(r, v), 0.0), Material.Shiness);
}
return ambient + diffuse + spec;
}
* The const qualifier
* Function overloading
* Passing arrays to structures to a function
Implementing two-sided shading
일반적으로 완전히 닫힌 mesh를 그릴 경우 polygon의 뒷면(back)은 보이지 않는다. 그러나 구멍이 있을 경우에는 뒷면이 보이는데 아래 예제에서는 normal vector가 잘못된 방향을 가리키고 있기 때문에 shade가 잘못 표현되어 있다. 이를 위해, normal vector를 뒤집은 후 계산해야 한다.
Implementing flat shading
- 고러드 셰이딩(Gouraud Shading): 부드러운 음영 효과를 내기 위해 다각형(polygon)의 면(face)에 색상이 보간(interpolated)됩니다.
- 플랫 셰이딩(Flat Shading): 각 다각형(polygon)을 단순히 하나의 색으로 채우는 것
Using subroutines to select shader functionality
- 서브루틴(Subroutine): binding a function call to one of a set of possible function definitions based on the value of a variable.
서브루틴의 함수 이름은 모두 같을 필요는 없지만, 같은 수와 타입의 매개변수(Parameter), 반환값(Return Type)을 가져야 한다. 런타임에 셰이더 프로그램(Shader Program)을 바꾸거나 다시 컴파일하는 것을 필요로 하지 않는다.
* 성능상의 이유로, 셰이더 프로그램에서 if와 같은 조건문(Conditional Statement)을 피하는 것은 중요하다. 최근 드라이버들은 조건문을 잘 핸들링하므로 서브루틴의 장점이 명확하진 않지만, uniform variable을 통한 조건 분기는 효율적이다.
subroutine vec3 shadeModelType(vec3 position, vec3 normal);
subroutine uniform shadeModelType shadeModel;
out vec3 LightIntensity;
// Uniform variables and attributes here
subroutine(shadeModelType)
vec3 phongModel(vec3 position, vec3 norm) {
// phong reflection model calculations go here..
}
subroutine(shadeModelType)
vec3 diffuseOnly(vec3 position, vec3 norm) {
// Compute diffuse shading only ..
}
void main() {
...
}
// programHandle variable contains the handle to the shader program object
GLuint phongIndex = glGetSubroutineIndex(programHandle, GL_VERTEX_SHADER, "phongModel");
GLuint diffuseIndex = glGetSubroutineIndex(programHandle, GL_VERTEX_SHADER, "diffuseOnly");
glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &phongIndex);
... // Render the left teapot
glUniformSubroutinesuiv(GL_VERTEX_SHADER, 1, &diffuseIndex);
... // Render the right teapot
Discarding fragments to create a perforated look
프래그먼트 셰이더(Fragment Shader)는 discard 키워드를 통해 프래그먼트(Fragment)를 버릴 수 있다. 이 키워드는 프래그먼트 셰이더가 더이상 수행되지 않도록 하며 depth를 포함한 모든 것을 output buffer에 기록하지 않는다.
int vec3 FrontColor;
in vec3 BackColor;
in vec2 TexCoord;
layout (location = 0) out vec4 FragColor;
void main() {
const float scale = 15.0;
bvec2 toDiscard = greaterThan(fract(TexCoord * scale), vec2(0.2, 0.2));
if (all(toDiscard))
discard;
if (gl_FrontFacing)
FragColor = vec4(FrontColor, 1.0);
else
FragColor = vec4(BackColor, 1.0);
}
Reference
이미지 출처 - https://static.packt-cdn.com/downloads/9781789342253_ColorImages.pdf