본문 바로가기

ComputerGraphics [고려대학교_한정현]

6강 OpenGLES 1

#version 300 es // 3.0버전

//[Input]
uniform mat4 worldMat, viewMat, projMat; // 모든 벡터에 동일하게 적용되는 uniform Input들
                                         // 각각 Transform(변환)을 나타낸다.
//[Input]
// layout 은 프로그램에서 저장되는 위치index.
layout(location =0) in vec3 position; 
layout(location =1) in vec3 normal;
layout(location =2) in vec2 texCoord;

//[Output]
out vec3 v_normal;
out vec2 v_texCoord;

void main(){
  gl_Position = projMat * viewMat * worldMat * vec4(position, 1.0); // object space에 있던 position 을 clip space로 옮김
                                                                    // 3x3의 position을 4x4로 바꾸는 과정 vec4(position ,1.0)
  v_normal = normalize(transpose ( inverse(mat3(worldMat))) * normal); //LT 인 worldMat에서 4x4를 왼쪽위 3x3행렬만 뽑아내면 L이 된다.
}

이제 코딩으로 어떻게 옮기는지 배우게 될 것이다.

 

GPU Rendering Pipline 

이전시간에 배운 위의 과정을 다 이해했고, Vertex Shader가 여러가지 일을 할수 있지만, 

그 중 꼭해야하는 가장 기본적인 변환들에 대해서 배웠었습니다.

[변환들 정리]

 - 여러개의 Object들이 여러개 있다고 해봅시다.

 - 각각 Object Space 는 다른 Object Space들과 전혀 관련이 없습니다.

 - 그래서 이 Object Space가 서로 관련 있을 수 있는 World Space로 모아야 합니다. 그래야 scene이  생성되는거에요.

 - 동일한 World Space로 모아졌으면, EYE AT UP 을 통해 Camera Space로 변환하는 View Transform 을 해야했다.

 - View Transform 은  WorldSpace와 다르게 모든 물체에 공히 적용된다.

 - 그다음 Clip Space에 옮기는 Projection Transform 을 적용한다. 

 - 각각의 Object들은 각각의 World Space를 가지지만,  하나의 CameraSpace(View)와 ClipSpace를 가진다.

 

[ Vertex, Index Arrays]

물체에 대해서 변환을 하나하나 정의해야하는데, 

물체를 생각해봅시다. 물체의 Vertex들에 대한 정보들은  Vertex Array에 있다.

Vertex Array 에는 Position 과 Normal 이 있다. (여기까진 배웠으나,)  Tex Coord라는 것도 있다.

이렇게 3개의 구성요소로 각각의 정점들이 정의가 되고 Array에 모여져있다..

 ( Tex Coord란 ? )

    ㄴ Texture라는 Polygonmesh 표면에 입혀지는 이미지 같은것이고, 그것들의 좌표들을 나타내는 것이 Tex Coord이다.  

 Vertex Shader는 정점을 하나씩 처리한다. ( Vertex Shader는 프로그램이고, 정점이 하나씩 input Parameter로 들어오는것)

 양이 굉장히 많기에, 하나에 하나씩이지만, GPU 에의해서 병렬적으로 처리된다.

 

 

[ Vertex Shader ,Fragment Shader]

이들은 프로그램이라고 했었죠. 

우리는 OpenGL ES 는 api 이기 때문에, 편하게 쉽게 가져다 쓰면 되지만,

이 두가지 쉐이더는 우리가 직접 코딩해주어야 합니다.

이는 GPU 에 아주 특화된 프로그램이기 때문에, 다른 언어를 사용합니다.

Shading Language 라고 합니다.

 

[GLSL]

 GPU 에서 알아듣는 쉐이더 Language 이다.

 가장 중요한 행렬과 벡터를 나타낼수 있는 자료구조를 제공한다. 

  - vec4  ( 4차원 벡터 )  

  - ivec4 ( 정수로 나타냄 )

  - mat3 mat4   3x3행렬 ,4x4행렬  3차원 변환을 4x4 행렬로 나타낼 수 있었다. (https://sbarrys.tistory.com/281?category=1084012 참고 )

 - mat3x4

 

[Vertex Shader 의 input 과 output]

  1. Input 

     1-1 . attributes : Vertex Array 를 구성하는 종류들 ( position, normal, texcoord ) ( 각 vertex 마다 다른 attribute가 들어간다. )

     1-2.  uniforms  : 모든것에 동일하게 적용되는 것들

                               world transform   (vertex 마다 다른 회전 변환을 적용하지 않을 것이잖아요)

                               view  transform    (vertex 마다 다른 회전 변환을 적용하지 않을 것이잖아요)

                               projection            (vertex 마다 다른 회전 변환을 적용하지 않을 것이잖아요)

 

  2. Output

     Clip Space Position 은 반드시 gl_Position 이라는 내장 변수에 출력해야 한다. ( 최종적으로 ClipSpace에 들어가기 때문 )

 

 [ Vertex Shader 첫 예시]

#version 300 es // 3.0버전

//[Input]
uniform mat4 worldMat, viewMat, projMat; // 모든 벡터에 동일하게 적용되는 uniform Input들
                                         // 각각 Transform(변환)을 나타낸다.
//[Input]
// layout 은 프로그램에서 저장되는 위치index.
layout(location =0) in vec3 position; 
layout(location =1) in vec3 normal;
layout(location =2) in vec2 texCoord;

//[Output]
out vec3 v_normal; //WorldSpace로 옮긴 법선벡터
out vec2 v_texCoord;

void main(){
  //Clip Space로 옮긴 position
  gl_Position = projMat * viewMat * worldMat * vec4(position, 1.0); // object space에 있던 position 을 Clip Space로 옮김
                                                                    // 3x3의 position을 4x4로 바꾸는 과정 vec4(position ,1.0)
  v_normal = normalize(transpose ( inverse(mat3(worldMat))) * normal); //LT 인 worldMat에서 4x4를 왼쪽위 3x3행렬만 뽑아내면 L이 된다.
                                                                       // 법선벡터의 WorldTransform = L의 역행렬에 전치행렬 해주고 normalize해주면 된다. 
                                                                       // https://sbarrys.tistory.com/283?category=1084012 참고
  v_texCoord = texCoord;
}

 

[OpenGL API]

-Vertex Shader를 다루기.

 1. OpenGL 은 shader object를 만듬. :  glCreateShader(type)
       GL_VERTEX_SHADER, GL_FRAGMENT_SHADER 타입을 받는다.

 2. Shader 변수는 unsignedInt인 GLunint로 생긴다.

 3. Shader 객체에 실제 shader를 저장한다. : glShaderSource(shader, 1, "gl소스코드..", NULL);

 4. 실제 shader가 저장된 쉐이더를 컴파일한다. : glCompileShader(shader); 

 

[Shader객체 만들어지면 program 객체로 통합해야함]

 GLuint program = glCreateProgram();

 glAttachShader(program, shader); // 지금은 vertexShader 만 붙임, 나중에 fragmentShader도 붙여야 한다. 

 glLinkProgram(program);

 glUseProgram(program); 

 

 

[Attributes 정의에 대한 내용]

 .obj 파일에는 polygon mesh 의 데이터들이 저장되어 있다.

   vertices 는 vertex array 를 가리키는 포인터

   indices 는 index array 를 가리키는 포인터 로 설정했다고 하자.

   그들이 objData 라는 구조체로 저장되어 있다고 해보자.

Vetex 와 Index의 자료형 설정

Index 는 unsignedShort 이고 , Vertex는 struct이다. 

3차원 position  , 3차원 normal , 2차원 좌표 texCoord

 

 

[Buffer Objects in GPU memory]

CPU 메모리에 있는 데이터(vertex, index arrays) 들은

 GPU에서 렌더링 하는 동안 사용할수 있도록 옮겨져야 한다.  ( 이 과정이 끝나면 attributes( pos, nor, tex ) 가 옮겨진다. )

이는 GPU 메모리의 buffer를 만든다 라고 표현한다.

각각의 array 는 GPU 메모리에서 다음과 같은 이름으로 불린다. 

  vertex array - > array buffer object 

  index array  - > element array buffer object

 

[CPU 에서 GPU 로 옮기는 과정에 대한 설명] -  VertexArray 를 옮기는 예제를 보이겠다.

 0. GLunit abo;

 1.  glGenBuffers (1, &abo ); // 1개의 오브젝트를 만들겠다. ( array Buffer Object라는 이름을 쓰겠다.)

 2.  glBindBuffer ( GL_ARRAY_BUFFER , abo); //생성된 버퍼에 Array_Buffer라고 정확히 바인딩 해준다.

 3.  glBufferData( GL_ARRAY_BUFFER ,          //실제 데이터를 넣어야한다.

                       (GLsizei) objData.verties.size() * sizeof(Vertex) ,

                       objData.vertices.data() , GL_STATIC_DRAW); 

 

[CPU 에서 GPU 로 옮기는 과정에 대한 설명] -  IndexArray 를 옮기는 예제를 보이겠다.

 0. GLunit eabo;

 1.  glGenBuffers (1, &eabo ); // 1개의 오브젝트를 만들겠다. ( array Buffer Object라는 이름을 쓰겠다.)

 2.  glBindBuffer ( GL_ARRAY_BUFFER , eabo); //생성된 버퍼에 Array_Buffer라고 정확히 바인딩 해준다.

 3.  glBufferData( GL_ARRAY_BUFFER ,          //실제 데이터를 넣어야한다.

                       (GLsizei) objData.indices.size() * sizeof(Index) ,

                       objData.indices.data() , GL_STATIC_DRAW); 

 

[Buffer에 저장된 attributes 들의 형태]

stride : 옮겨서 다음것을 가져오라 라는말.

 pos, nor, tex는 어디서 시작한다. 라는것을 쉐이더에게 알려줘야 한다.

 이때 다음 pos를 가리키는 위치 는 [pos 를 가리키는 위치 + stride]  가 된다. 

position, normal , texturecoordinates 에 대한 위치

 

 

[ 쉐이더 내부의 Uniform 값은 OpenGLES가 정의해준다.]

3가지 uniforms 이 있었다. 
uniform mat4 worldMat, viewMat, projMat; // 모든 벡터에 동일하게 적용되는 uniform Input들
                                         // 각각 Transform(변환)을 나타낸다.

카메라도 고정되어있고, 물체도 고정되어 있는 경우는 거의 없죠.

- 우리 환경은 물체가 막 움직이고 시점도 바뀌죠.

 여러가지 Animation 이 적용되어야 한다. ( 회전, 이동, scale 등)  

 #위치가 변하면 > 각 Object 의 World Matrix 가 매 프레임마다 변하는거죠. 그거는 openGLES 에서 우리가 설정해주어야 한다.

 #물체가 고정되어있어도, 카메라가 움직인다? 그러면 매 프레임마다 ViewMatrix 가 변화해야하죠. 


 자,  "위치가 변하는 경우"에  매프레임마다 worldMat에 Animation 을 적용시켜 주어야 한다고 했죠.

 OpenGLES 가 Animation 을 적용한 값을 

 Shader 내부의 uniform 값인 worldMat 에 넣는 예제입니다.

 GLint loc = glGetUniformLocation ( program, "worldMat" );  //쉐이더가 링킹된 프로그램에서 worldMat을 찾아주어야 합니다.  
 glUniformMatrix4fv ( loc, 1, GL_FALSE, glm::value_ptr(worldMatrix)); // 쉐이더가 가지고 있는 worldMat위치에 openGLES 가 가지고 있는 worldMatrix 변수를 심어준다.  

 

[이제는 그리는 준비를 마쳤다]

 1) Attributes ( Pos, Nor, Tex Coor )                // GPU 의 Buffer에 옮기고, 주소를 짚어주는 stride까지 해봤다.

 2) Uniform   ( worldMat, viewMat, projMat )  // 프레임마다 Shader의 값을 변경시켜주는 것까지 했다.

 

 

[DrawCalls]

 non indexed representation 을 예시로 들어보겠다.  (인덱스 어레이 없는 무식한 VertexArray)

 구 mesh 가 정의되어 있다고 보자. 구는 48  개의 삼각형으로 만들어졌다.

 144개의 vertex원소가 vertex Array에 있을것이다. 

 glDrawArrays( GL_TRIANGLES, 0 ,144); // 144개의 버텍스를 이용해 삼각형을 그려나간다. 

 

[indexed 표현법의 DrawCalls]

glDrawElement