- OpenGL Shader Language
- Shader : GPU상에서 동작하는 그림을 그리기 위한 작은 프로그램
- 정점별 / 픽셀별로 병렬 수행되어 성능을 높임
- GLSL : OpenGL에서 Shader를 작성하기 이해 제공하는 C기반의 언어
- 그외 대표적인 shader language
- HLSL : DirectX용 shader language
- Metal : Metal용 shader language
- cg : nVidia가 제시한 shader language
#version version_number
in type in_variable_name;
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;
void main()
{
// process input(s) and do some weird graphics stuff ...
// output processed stuff to output variable
out_variable_name = weird_stuff_we_processed;
}
in, out, uniform : '한정자' 라고 부른다. 변수의 용도를 지정해준다.
'.x' , '.y' , '.z' , '.w'인덱스를 사용한다.
swizzling 가능
- 얻어오고 싶은 인덱스를 연속으로 쓰기
- ex) .xyz => vec3 (같은 의미)
- RGBA와 STPQ도 동일한 방식으로 사용 가능하다.
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
- Shader의 입출력 구조
- 모든 shader는 용도에 맞는 입출력이 선언되어있어야 한다.
- in, out : shader의 입출력을 가리키는 Type Qualifier
Vertex shader
- 각 정점별로 설정된 Vertex Attribute를 입력받는다
- 몇번째 Attribute를 입력받을지에 대한 추가적인 설정을 할 수 있다.
layout( location = 0 )
- 반드시 정점의 출력위치 gl_Position값을 계산해줘야 한다.
Rasterization
- Vertex Shader의 출력값을 primitive에 맞게 보간하여 픽셀별 값으로 변환(Rasterization)
primitive : 내가 삼각형을 그린다, 이러면 삼각형이 primitive이다.
Fragment Shader
- Rasterization과정을 거쳐 픽셀별로 할당된 Vertex Shader의 출력값이 입력됨
- 각 픽셀의 실제 색상값이 출력되어야 한다.
코드 수정 , shader / simple.vs 작성
#version 330 core
layout (location = 0) in vec3 aPos; // 0번째 attribute가 정점의 위치
out vec4 vertexColor; // fragment shader로 넘어갈 컬러값
void main() {
gl_Position = vec4(aPos, 1.0); // vec3를 vec4 생성자에 사용
vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 어두운 빨간색을 출력값으로
}
#version 330 core
in vec4 vertexColor; // vs로부터 입력된 변수 (같은 변수명, 같은 타입)
out vec4 FragColor; // 최종 출력 색상
void main() {
FragColor = vertexColor;
}
출력값 : 어두운 색상을 가진 도형이 출력된다.
- 진행순서
- shader / simple.vs : main함수에서 vertexColor지정
- shader / simple.vs : 지정된 값이 out vec4 vertexColor로 인하여 fragment Shader로 넘어간다(out).
- shader / simple.fs : simple.vs로 부터 넘어온 값이 in vec4 vertexColor에 입력된다(in).
- shader / simple.fs : main함수에서 FragColor를 vertexColor로 저장후 넘겨준다(out).
shader에 전달 가능한 Globla Value.
- 병력로 수행되는 모든 shader thread들이 동일한 값을 전달 받는다.
변수 선언 앞에 uniform Type Qualifier를 사용하여 선언
#version 330 core
uniform vec4 color;
out vec4 fragColor;
void main() {
fragColor = color;
}
#version 330 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos, 1.0);
}
uniform variable에 값을 입력하는 과정
m_program = Program::Create({fragShader, vertShader});
if (!m_program)
return false;
SPDLOG_INFO("program id: {}", m_program->Get());
auto loc = glGetUniformLocation(m_program->Get(), "color"); // 추가
m_program->Use(); // 추가
glUniform4f(loc, 1.0f, 1.0f, 0.0f, 1.0f); // 추가
glClearColor(0.1f, 0.2f, 0.3f, 0.0f);
- glGetUniformLocation(program id, uniform name) : 'uniform name'이라는 이름의 변수가 어디에 있는지 가지고 와서 정수로 저장된다.
- niform name : string Type- glUniform4f(GLint location, GLfloat, GLfloat, GLfloat, GLfloat) : GLint location의 위치에 있는 변수에 뒤의 값을 적용한다.
- 프레임마다 uniform값을 변경시켜보기
Context::Render()
id Context::Render() { glClear(GL_COLOR_BUFFER_BIT); static float time = 0.0f; float t = sinf(time) * 0.5f + 0.5f; auto loc = glGetUniformLocation(m_program->Get(), "color"); m_program->Use(); glUniform4f(loc, t*t, 2.0f*t*(1.0f-t), (1.0f-t)*(1.0f-t), 1.0f); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); time += 0.016f; }
- position
- normal
- tangent
- color
- texture coordinates
float vertices[] = {
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // 앞 3 : top right, 뒤 3: red
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // 앞 3 : bottom right, 뒤 3: green
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // 앞 3 : bottom left, 뒤 3: blue
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 앞 3 : top left, 뒤 3: yellow
};
...
m_vertexLayout = VertexLayout::Create();
m_vertexBuffer = Buffer::CreateWithData(GL_ARRAY_BUFFER, GL_STATIC_DRAW, vertices, sizeof(float) * 24);
...
m_vertexLayout->SetAttrib(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, 0); //첫번째 Attributes
m_vertexLayout->SetAttrib(1, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, sizeof(float) * 3); //두번째 Attributes
하나의 vertex의 크기가 색상의 float값만큼 늘어났다.
두번째 Attribute인 색상이 시작되는 지점을 잡아주어야 한다. 그래서 'sizeof(float) * 3'가 추가되는것이다.
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
out vec4 vertexColor;
void main() {
gl_Position = vec4(aPos, 1.0);
vertexColor = vec4(aColor, 1.0);
}
하나의 vertex를 attribute만큼 나누어 보내준다.
#version 330 core
in vec4 vertexColor;
out vec4 fragColor;
void main() {
fragColor = vertexColor;
}
...
ShaderPtr vertShader = Shader::CreateFromFile("./shader/per_vertex_color.vs", GL_VERTEX_SHADER);
ShaderPtr fragShader = Shader::CreateFromFile("./shader/per_vertex_color.fs", GL_FRAGMENT_SHADER);
...
//glClearColor()은 동작하지 않는다.