전통적인 그래픽스 파이프라인
예전에는 모두 fixed vlsi칩으로 구현했기 때문에 수정할 수 없다.
Vertex input | Vertex Processing |
primitive assembly |
rasterization | fragment processing |
blend | framebuffer |
fixed hardware |
Vertex Processing
- 입력받은 Vertex의 좌표 변환(transform)
- 필요하다면 위치를 조금씩 수정한다.
Primitive assembly
- vertex(꼭지점)를 결합한다. (graphics primitive)
- 1vertex(점), 2vertex(선), 3vertex(삼각형)
- 컴퓨터 테크픽스 관점에서는 다른 다각형들을 모두 삼각형으로 분해할 수 있다. 그래서 삼각형으로 최적화하려는 목적이 있다.
Rasterization(래서터화)
- Primitive에 그려진 삼격형을 pixel로 선정한다. 즉 픽셀의 집합으로 만든다.
- primitive에서 만든 기하학적인 삼각형을 pixel로 변형한다.
Fragment processing
- Rasterization에서 변형한 pixel에 관련자료(색상, 깊이, 위치....)를 합한다.
- 해당 픽셀이 하나하나 색상이 입혀져 화면에 켜지며 어떤 형태로 자리 잡게 된다.
blend(후처리 단계)
- 후처리 단계로 fragment 단위 처리로 최종 나온 삼각형에 다양한 효과를 준다 ex) 안개효과 등.
현재 프로그래머블 파이프라인(Shader 프로그램)
vertex processing vlsi를 shader로 대체
fragment processing vlsi를 shader로 대체
Vertex input | Vertex Shader |
primitive assembly |
rasterization | fragment Shader |
blend | framebuffer |
전통적인 그래픽스 파이프라인의 속도를 계선하기 위해 병렬 처리를 도입하였는데 이때 vertex processing과 fragment processing에서 속도가 느려지는 현상이 발생하였다. 이때 쉐이더를 사용하여 프로그램을 개발하는 파이프라인을 프로그래머블 파이프라인이라고 한다.
Shader program
- GPU에서 돌아가는 small size 프로그램으로 병렬 처리된다.
Shader Language
- 쉐이더를 만들기 위한 프로그래밍 언어
Shader compiler
- 쉐이더 전용 컴파일러
shader Programming 종류
DirectX HLSL - PC와 XBox에서 사용가능
Cg(c for Graphics) - nvidia 그래픽카드에서만 작동
OpenGL SL - 거의 모든 GPU에서 사용가능(GLSL)
GLSL(OpenGL Shader Language)
GLSL은 C 프로그래밍 언어를 기반으로 하며, 다음과 같은 주요 특징들을 가지고 있습니다.
- 벡터(vector) 및 행렬(matrix) 기본 자료형 지원: GLSL은 벡터 및 행렬 연산을 지원하여 3D 그래픽 처리에 편리합니다.
- 내장 함수: 삼각 함수, 로그 함수, 행렬 연산 등과 같은 다양한 내장 함수를 제공합니다.
- Pointer 자료형이 없음. (gpu 병렬처리를 위해서는 pointer는 필요 없음)
- float의 정밀도 조정 : highp, mediump, lowp의 precision qualifier를 사용하여 속도를 조절할 수 있다.(ex precision lowp float;)
- register에 대한 qualifier : shader 별로 input, ouput에 따라 사용
1. 버텍스 셰이더(Vertex Shader): 입력 정점 데이터를 받아들여 정점의 위치, 색상, 텍스처 좌표 등을 변형하는 셰이더입니다. 버텍스 셰이더는 정점의 기하학적 변형을 담당하며, 이러한 변형은 보통 모델 변환, 카메라 변환 및 투영 변환과 같은 단계에서 이루어집니다.
attribute0, 1, 2, 3 -> | vertex shader processor -> | varying0, 1, 2, 3 |
In 메모리 - attribute register에 저장된다.
Out 메모리 - varying register에 출력된다.
in vec4 vertexPos;
void main(void)
{
gl_Position = vertexPos;
}
vertexPos
in : attribute register
out : varying register
gl_Position : vertex position을 저장
#version 330 core //3.3버전의 core를 사용하겠다 익스트렉터사용x
GLflot vertPos[] ={
-0.5f, -0.5f, 0.0f, 1.0f,
+0.5f, -0.5f, 0.0f, 1.0f
-0.5f, +0.5f, 0.0f, 1.0f
}
위에 선언한 GLflot의 vertexPos 3개는 각각 하나의 프로세서가 병렬로 처리하게 된다.
2. 프래그먼트 셰이더(Fragment Shader): 렌더링 된 버텍스들 사이의 픽셀을 채우는 역할을 합니다. 픽셀의 최종 색상, 광원 모델, 그림자 효과 및 텍스처 매핑 등을 계산하여 픽셀의 색상을 결정합니다.
varying0, 1, 2, 3 -> | fragment shader processor -> | output |
In 메모리 - varying register 저장
Out - framebuffer update 출력
#version 330 core //3.3버전의 core를 사용하겠다 익스트렉터사용x
out vec4 FragColor;
void main(void){
FragColor = vec4(1.0, 0.0, 0.0, 1.0);//color red
}
vertexPos
in : varying register
out : framebuffer update
forColor당 하나의 프로세서가 병렬로 처리하게 된다.
vs(Vertex Shader) 소스코드와 fs(Fragment Shader) 소스코드 사용법
vs소스코드와 fs소스코드 쉐이더 변환 과정
- glCreateShader
- glShaderSource
- glCompileShader
- 결괏값 각각의 shader
const char* vertSource =
"#version 330 core \n\
in vec4 vertexPos; \n\
void main(void) { \n\
gl_Position = vertexPos; \n\
}";
const char* fragSource =
"#version 330 core \n\
out vec4 FragColor; \n\
void main(void) { \n\
FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n\
}";
GLuint vert = 0; // vertex shader ID number
GLuint frag = 0; // fragment shader ID number
GLuint prog = 0; // shader program ID number
void initFunc(void) {
// vert: vertex shader
vert = glCreateShader(GL_VERTEX_SHADER);//쉐이더 오브젝트 생성
glShaderSource(vert, 1, &vertSource, NULL);//쉐이더 소스코드를 준다
glCompileShader(vert); // compile to get .OBJ
// frag: fragment shader
frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag, 1, &fragSource, NULL);
glCompileShader(frag); // compile to get .OBJ
// prog: program
prog = glCreateProgram();//프로그램 id번호 출력
glAttachShader(prog, vert);
glAttachShader(prog, frag);
glLinkProgram(prog); // link to get .EXE
// execute it!
glUseProgram(prog);//그래픽카드 실행
}
glUseProgram에서 만든 쉐이더를 이용하여 exe를 만들어 그래픽카드에서 동작한다.
생성한 쉐이더 gpu와 c언어 cpu와 연결
vertPos에서 선언한 3개의 포지션은 각각 위에서 만든 vertSource로 연결되어 gpu에서 병렬로 처리된다.
ex) in vect vertexPos = {-0.5F, -0.5F, 0.0F, 1.0F} - 1번 프로세싱
ex) in vect vertexPos = {+0.5F, -0.5F, 0.0F, 1.0F} - 2번 프로세싱
ex) in vect vertexPos = {-0.5F, +0.5F, 0.0F, 1.0F} - 3번 프로세싱
GLfloat vertPos[] = {
-0.5F, -0.5F, 0.0F, 1.0F,
+0.5F, -0.5F, 0.0F, 1.0F,
-0.5F, +0.5F, 0.0F, 1.0F,
};
void drawFunc(void) {
// clear in gray color
glClear(GL_COLOR_BUFFER_BIT);
// provide the vertex attributes
GLuint loc = glGetAttribLocation(prog, "vertexPos");//vertexPos이 할당된 에트리뷰트의 위치를 찾는다.
glEnableVertexAttribArray(loc);//찾은 에트리뷰트 실행 c언어 프로그램과 연결
glVertexAttribPointer(loc, 4, GL_FLOAT, GL_FALSE, 0, vertPos);//미리 선언한 carray를 보낸다 vertPos를 보내게 된다. c언어의 cpu와 gpu가 연결된상황
// draw a triangle
glDrawArrays(GL_TRIANGLES, 0, 3);//실제로 그리는 부분
// done
glFinish();//opengl의 모든 실행을 완료시킨다.
}
1.2 버전 옛날 vs(Vertex Shader) 소스코드와 fs(Fragment Shader) 소스코드
#version 120
attribute vec4 vertexPos;
void main(void) {
gl_Position = vertexPos;
}
#version 120
void main(void) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
layout을 사용하여 vs와 fs 메모리 직접지정
vs는 attribute에 저장되며 fs는 varying에 저장된다. 이를 컴파일에서 자동저장이 아닌 직접저장을 할 수 있다.
#version 330 core
layout (location = 5) in vec4 vertexPos; // attribute
void main(void) {
gl_Position = vertexPos;
}
#version 330 core
layout (location = 0) out vec4 FragColor; // fragment color: framebuffer
void main(void) {
FragColor = vec4(1.0, 0.0, 0.0, 1.0); // red color
}
Uniforms
uniform Global Variable 이다. Global은 uniform은 shader program 객체에서 고유한 변수로 shader program의 모든 단계의 모든 shader에서 접근 가능합니다.
uniform 변수에 어떤 값을 설정하든 리셋을 하거나 업데이트를 하기 전까지 그 값을 계속 유지하고 있습니다.
자세히 설명하면, Uniform은 OpenGL에서 셰이더 프로그램에 값을 전달하는 메커니즘입니다. 이것은 셰이더 프로그램 내에서 상수값이나 변하지 않는 데이터를 전달하는 데 사용됩니다. 예를 들어, 변환 행렬, 빛의 위치, 재질 속성 등의 값들이 이에 해당합니다.
Uniform은 다음과 같은 특징을 갖습니다:
- 값이 변하지 않음(Global): Uniform은 한 번 설정되면 해당 프레임의 렌더링 동안 고정됩니다. 이는 셰이더 프로그램이 동일한 값을 모든 정점 또는 프래그먼트에 적용할 수 있도록 해줍니다.
- 셰이더 프로그램 내에서 선언: Uniform은 셰이더 프로그램의 코드 내에서 uniform 키워드를 사용하여 선언됩니다. 예를 들어, uniform mat4 modelViewProjectionMatrix;와 같이 사용됩니다.
- 값 설정: Uniform의 값은 OpenGL API를 사용하여 설정됩니다. 이를 통해 CPU에서 값을 변경하고 셰이더 프로그램에 전달할 수 있습니다. glUniform 시리즈의 함수를 사용하여 값을 설정할 수 있습니다.
- 변수 타입: Uniform은 다양한 타입을 가질 수 있습니다. 예를 들어, 행렬, 벡터, 스칼라 등이 될 수 있습니다.
#version 330 core
in vec4 aPos; // vertex position: attribute
in vec4 aColor; // vertex color: attribute
out vec4 vColor; // varying color: varying
uniform vec4 uMove; // movement vector: uniform
void main(void) {
gl_Position = aPos + uMove;
vColor = aColor;
}
aPos와 uMove를 더하여 새로운 포지션인 gl_Position을 만들어 낸다.
// draw the first triangle
GLuint locMove = glGetUniformLocation(prog, "uMove");//uniform위치 얻기
glUniform4f(locMove, -0.5F, -0.5F, 0.0F, 0.0F);//uniform 값 적용
glDrawArrays(GL_TRIANGLES, 0, 3);//pos + uniform 위치로 삼각형 생성
// draw the second triangle
glUniform4f(locMove, 0.0F, 0.0F, 0.0F, 0.0F);
glDrawArrays(GL_TRIANGLES, 0, 3);
GLSL(쉐이더프로그램) 디버그
glGetShaderiv
이 함수를 사용하면 셰이더의 컴파일 상태, 유형 및 정보 로그 길이와 같은 여러 속성을 쿼리할 수 있습니다. 함수 시그니처는 일반적으로 다음과 같습니다.
void glGetShaderiv(GLuint shader, GLenum pname, GLint *params);
- shader: 셰이더 객체의 이름(식별자).
- pname: 조회할 매개변수를 지정합니다. 이는 GL_SHADER_TYPE, GL_COMPILE_STATUS, GL_INFO_LOG_LENGTH 등과 같은 사전 정의된 상수 중 하나일 수 있습니다.
- params: 검색된 값이 저장될 변수를 가리킵니다.
예를 들어, 셰이더가 성공적으로 컴파일되었는지 여부를 확인하려면 pname으로 GL_COMPILE_STATUS를 사용합니다. 그런 다음 값은 params 변수에 저장되며, 컴파일이 성공했는지 여부를 결정하기 위해 해당 값을 확인할 수 있습니다.
void glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog);
- shader: 정보를 가져올 셰이더 객체의 이름(식별자).
- maxLength: 가져올 정보 로그의 최대 길이.
- length: 실제로 가져온 정보 로그의 길이를 저장하는 변수.
- infoLog: 정보 로그가 저장될 문자열 버퍼.
예를 들어, 셰이더 컴파일 과정에서 발생한 오류 및 경고 메시지를 확인하려면 이 함수를 사용하여 해당 정보를 가져올 수 있습니다. 가져온 정보는 infoLog 문자열에 저장되며, 실제로 가져온 문자열의 길이는 length 변수에 저장됩니다.
void glGetProgramiv(GLuint program, GLenum pname, GLint *params);
- program: 매개변수를 검색할 프로그램 객체의 이름(식별자).
- pname: 조회할 매개변수를 지정하는 상수입니다. 예를 들어, GL_LINK_STATUS, GL_ATTACHED_SHADERS, GL_INFO_LOG_LENGTH 등이 있습니다.
- params: 검색된 값이 저장될 변수를 가리킵니다.
예를 들어, 프로그램의 링크 상태를 확인하려면 GL_LINK_STATUS를 pname으로 사용하여 해당 값을 가져올 수 있습니다. 가져온 값은 params 변수에 저장되며, 프로그램의 링크 상태를 판단하기 위해 해당 값을 확인할 수 있습니다.
glGetProgramInfoLog
이 함수를 사용하면 프로그램 링크 과정에서 발생한 오류 및 경고 메시지를 확인할 수 있습니다.
link와 validation 로그 정보 메세지로 받아옴
void glGetProgramInfoLog(GLuint program, GLsizei maxLength, GLsizei *length, GLchar *infoLog);
- program: 정보를 가져올 프로그램 객체의 이름(식별자).
- maxLength: 가져올 정보 로그의 최대 길이.
- length: 실제로 가져온 정보 로그의 길이를 저장하는 변수.
- infoLog: 정보 로그가 저장될 문자열 버퍼.
예를 들어, 프로그램 링크 과정에서 발생한 오류 및 경고 메시지를 확인하려면 이 함수를 사용하여 해당 정보를 가져올 수 있습니다. 가져온 정보는 infoLog 문자열에 저장되며, 실제로 가져온 문자열의 길이는 length 변수에 저장됩니다.
glValidateProgram
OpenGL에서 프로그램 객체를 유효성 검사하는 데 사용되는 함수입니다. 이 함수를 호출하면 OpenGL은 현재 프로그램 객체를 검사하여 현재 OpenGL 컨텍스트의 상태와 일치하는지 확인합니다.
void glValidateProgram(GLuint program);
- program: 유효성을 검사할 프로그램 객체의 이름(식별자).
glValidateProgram을 호출하면 OpenGL은 프로그램 객체의 유효성을 검사하고, 유효성 검사 결과는 프로그램 객체의 상태에 반영됩니다. 유효성 검사가 성공적으로 완료되면 GL_TRUE를 반환하고, 그렇지 않으면 GL_FALSE를 반환합니다.
프로그램 객체를 사용하기 전에 유효성 검사를 수행하는 것은 권장되는 방법 중 하나입니다. 유효성 검사를 통해 프로그램이 현재 OpenGL 컨텍스트와 호환되는지 확인할 수 있습니다.
'OpenGL' 카테고리의 다른 글
[OpenGL] GLFW time 함수 (0) | 2024.04.16 |
---|---|
[OpenGL] OpenGL와 GLM(OpenGL Mathematics) (0) | 2024.04.16 |
[OpenGL] 래스터 저장방식과 컬러 (0) | 2024.04.03 |
[OpenGL] Delayed execution 지연실행와 glFlush, glFinish (0) | 2024.04.02 |
[OpenGL] Double buffering와 glfwSwapBuffers (0) | 2024.04.02 |