Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Archives
Today
Total
관리 메뉴

free garden

Chap 3. The Basics of GLSL Shaders 본문

Note/Graphics

Chap 3. The Basics of GLSL Shaders

KUflower 2019. 11. 5. 00:11

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는 앱에서 전달하기도 하고, 이전 단계의 파이프라인(다른 셰이더)에서 전달되기도 한다. 

vertex, fragment shader 간에 input, output 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를 뒤집은 후 계산해야 한다.

우: two-sided rendering 기법을 이용

 

Implementing flat shading

- 고러드 셰이딩(Gouraud Shading): 부드러운 음영 효과를 내기 위해 다각형(polygon)의 면(face)에 색상이 보간(interpolated)됩니다.

- 플랫 셰이딩(Flat Shading): 각 다각형(polygon)을 단순히 하나의 색으로 채우는 것

좌: Gouraud, 우: Flat

 

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에 기록하지 않는다.

discard를 이용해 blending을 사용하지 않고 다각형에 구멍을 만들 수 있다. 이때, depth에 따라 정렬을 해야하는 등 blending을 이용하면 고려해야할 것 들을 모두 신경쓰지 않아도 된다.

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

Comments