GLSL

대인공·2022년 8월 1일
0

OpenGL

목록 보기
3/8
post-thumbnail

GLSL이란

  • OpenGL Shader Language
    - Shader : GPU상에서 동작하는 그림을 그리기 위한 작은 프로그램
    - 정점별 / 픽셀별로 병렬 수행되어 성능을 높임
    - GLSL : OpenGL에서 Shader를 작성하기 이해 제공하는 C기반의 언어
  • 그외 대표적인 shader language
    - HLSL : DirectX용 shader language
    - Metal : Metal용 shader language
    - cg : nVidia가 제시한 shader language
  • GLSL source Code의 대략적인 구조
#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 : '한정자' 라고 부른다. 변수의 용도를 지정해준다.

  • GLSL에서 사용 가능한 데이터 타입
    - 기본 타입 : int, float, double, uint, bool
    - 벡터 타입 :
    1. vecX : float형 벡터
    2. bvecX : bool형 벡터
    3. ivec : int형 벡터
    4. uvecX : uint형 벡터
    5. dvecX : double형 벡터
      (X에는 2,3,4 사용가능)
    - 행렬 타입
    1. matX : float형 행렬
    2. bmatX : bool형 행렬
    3. imatX : int형 행렬
    4. umatX : uint형 행렬
    5. dmatX : double형 행렬
      (X에는 2,3,4 사용가능)

Vector

'.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);

In / Out

  • 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); // 어두운 빨간색을 출력값으로
}
  • 코드 수정 , shader / simple.fs 작성
#version 330 core

in vec4 vertexColor; // vs로부터 입력된 변수 (같은 변수명, 같은 타입)
out vec4 FragColor; // 최종 출력 색상

void main() {
  FragColor = vertexColor;
}

출력값 : 어두운 색상을 가진 도형이 출력된다.

  • 진행순서
  1. shader / simple.vs : main함수에서 vertexColor지정
  2. shader / simple.vs : 지정된 값이 out vec4 vertexColor로 인하여 fragment Shader로 넘어간다(out).
  3. shader / simple.fs : simple.vs로 부터 넘어온 값이 in vec4 vertexColor에 입력된다(in).
  4. shader / simple.fs : main함수에서 FragColor를 vertexColor로 저장후 넘겨준다(out).

Uniform

shader에 전달 가능한 Globla Value.
- 병력로 수행되는 모든 shader thread들이 동일한 값을 전달 받는다.
변수 선언 앞에 uniform Type Qualifier를 사용하여 선언

  • 코드 수정 , shader / simple.fs 작성
#version 330 core
uniform vec4 color;
out vec4 fragColor;

void main() {
    fragColor = color;
}
  • 코드 수정 , shader / simple.vs 작성
#version 330 core
layout (location = 0) in vec3 aPos;

void main() {
  gl_Position = vec4(aPos, 1.0);
}

Uniform값 전달 받기

  • uniform variable에 값을 입력하는 과정

    1. glGetUniformLocation()을 사용하여 program Object로 부터 uniform handle을 얻는다.
    2. program이 바인딩된 상대에서 glUniform...()이라는 함수를 사용하여 값을 입력한다
    • Context :: Init() , src / context.cpp 작성
      (Program인스턴스 생성 이후 uniform값 입력과정 추가)
	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;
    }

Vertex Attributes

  1. position
  2. normal
  3. tangent
  4. color
  5. texture coordinates

vertex별로 다른 색상을 가진 사각형 그려보기

  • Context::Init() , src / context.cpp 작성
    (vertices 정보 변경)
	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'가 추가되는것이다.

  • simple.vs를 다른 파일로 교체 , shader/per_vertex_color.vs 생성
#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만큼 나누어 보내준다.

  • simple.fs를 다른 파일로 교체 , shader/per_vertex_color.fs 생성
#version 330 core
in vec4 vertexColor;
out vec4 fragColor;

void main() {
    fragColor = vertexColor;
}
  • Context :: init() 수정 , src / context.cpp 작성
    (새로만든 파일로 경로를 변경해준다.)
	...
	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()은 동작하지 않는다.

정리

  • GLSL
    - c와 비슷한 문법체계의 shader용 language
    - 기본적인 수치값 타임 외에 벡터, 행렬 타입이 있다.
    - in, out을 시용하여 각 shader의 입출력을 지정해준다.
    - vertex shader의 경우 layout을 사용하여 attribute index를 지정해준다.
    - vertex shader의 경우 gl_Position을, fragment shader의 경우 픽셀의 최종 색상값을 출력해야한다.
    - vertex shader의 out변수들은 Rasterization과정을 거쳐 픽셀단위로 보간되어 frament shader의 in 변수들로 입력된다.
    - uniform variable를 사용하면 shader에서 사용할 수 있는 Global값을 설정할 수 있다.
    - glGetShaderLocation()으로 uniform handle을 얻을 수 있다.
    - program이 바인딩된 상태에서 glUniform...()으로 uniform variable의 값을 설정 할 수 있다.
    - 필요에 따라 정점별로 여러개의 Attribute를 설정하여 사용가능하다.
profile
이제 막 시작하는 유니티 클라이언트

0개의 댓글

관련 채용 정보