#define MATRIX1_LOCATION 2
#define MATRIX2_LOCATION 3
#define MATRIX3_LOCATION 4
#define MATRIX4_LOCATION 5
void Cube::InitModel()
{
// Create VBO
size = 24*sizeof(float);
glGenBuffers(1, &vId);
glBindBuffer( GL_ARRAY_BUFFER, vId );
glBufferData( GL_ARRAY_BUFFER, size + size, 0, GL_STATIC_DRAW );
glBufferSubData( GL_ARRAY_BUFFER, 0, size, cubeVerts );
glBufferSubData( GL_ARRAY_BUFFER, size, size, cubeColors );
// Create VBO for transformation matrix
glGenBuffers(1, &matrixId);
glBindBuffer( GL_ARRAY_BUFFER, matrixId );
glm::mat4 transformMatrix[dimension][dimension][dimension];
glBufferData(GL_ARRAY_BUFFER, sizeof(transformMatrix) , 0, GL_DYNAMIC_DRAW);
// Create IBO
unsigned short indexSize = sizeof( unsigned short )*36;
glGenBuffers(1, &iId);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, iId );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, indexSize, 0, GL_STATIC_DRAW );
glBufferSubData( GL_ELEMENT_ARRAY_BUFFER, 0, indexSize, cubeIndices );
glGenVertexArrays(1, &Vertex_VAO_Id);
glBindVertexArray(Vertex_VAO_Id);
// Create VBO and set attribute parameters
glBindBuffer( GL_ARRAY_BUFFER, vId );
glEnableVertexAttribArray(VERTEX_LOCATION);
glEnableVertexAttribArray(COLOR_LOCATION);
glVertexAttribPointer(VERTEX_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glVertexAttribPointer(COLOR_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, (void*)size);
// Create VBO for transformation matrix and set attribute parameters
glBindBuffer( GL_ARRAY_BUFFER, matrixId );
glEnableVertexAttribArray(MATRIX1_LOCATION);
glEnableVertexAttribArray(MATRIX2_LOCATION);
glEnableVertexAttribArray(MATRIX3_LOCATION);
glEnableVertexAttribArray(MATRIX4_LOCATION);
glVertexAttribPointer(MATRIX1_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * 0));
glVertexAttribPointer(MATRIX2_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * 4));
glVertexAttribPointer(MATRIX3_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * 8));
glVertexAttribPointer(MATRIX4_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * 12));
glVertexAttribDivisor(MATRIX1_LOCATION, 1);
glVertexAttribDivisor(MATRIX2_LOCATION, 1);
glVertexAttribDivisor(MATRIX3_LOCATION, 1);
glVertexAttribDivisor(MATRIX4_LOCATION, 1);
// Bind IBO
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, iId );
// Make sure the VAO is not changed from outside code
glBindVertexArray(0);
return;
}
glVertexAttribPointer(MATRIX1_LOCATION, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (void*)(sizeof(float) * 0));
세번째 인자는 stride 이고, 4번째는 offset 이다.
stride는 정점마다 간격이 얼마나 되는지 써줘야 하므로 sizeof(mat4)가 맞고,
offset은 mat4의 첫번째열 두번째열 세번째열 네번째열로 바인딩 해야 하기 떄문에,
0, 16, 32. 48 로 해주면된다.
glVertexAttribDivisor는 인스턴스 당 행렬을 갱신해야 되므로, 두번째 인자를 1로 준다.
glVertexAttribDivisor는 인스턴스 렌더링(instance rendering) 시에 각 속성이 얼마나 자주 갱신되어야 하는지 지정하는 함수입니다. 이 함수는 여러 인스턴스를 동시에 렌더링할 때, 각 인스턴스에 대해 정점 속성(예: 위치, 색상, 변환 행렬 등)을 다르게 설정할 수 있도록 합니다.
void glVertexAttribDivisor(GLuint index, GLuint divisor);
- index: 속성의 위치로, glVertexAttribPointer로 지정한 속성 인덱스와 동일합니다.
- divisor: 이 속성이 얼마나 자주 갱신될지를 나타내는 값입니다.
divisor 값의 의미
- divisor = 0: 속성이 각 정점마다 갱신됩니다. 이는 일반적인 VBO처럼 작동하여, 하나의 정점마다 속성 데이터가 변경됩니다.
- divisor = 1: 속성이 각 인스턴스마다 갱신됩니다. 예를 들어, 변환 행렬처럼 각 인스턴스마다 다르게 설정할 필요가 있는 속성에 사용합니다.
- divisor = n: 속성이 매 n개의 인스턴스마다 갱신됩니다. 예를 들어, divisor = 2라면 2개의 인스턴스마다 속성이 한 번씩 갱신됩니다.
glVertexAttribDivisor의 용도
glVertexAttribDivisor는 인스턴스별로 고유한 속성을 지정할 때 필수적입니다. 예를 들어, 100개의 큐브 인스턴스를 각각 다른 위치와 회전으로 렌더링하려는 경우, 각 인스턴스마다 고유한 model 변환 행렬을 제공해야 합니다.
이런 상황에서 glVertexAttribDivisor를 통해 속성이 인스턴스마다 갱신되도록 설정하면, glDrawElementsInstanced나 glDrawArraysInstanced와 같은 함수 호출을 한 번으로 여러 인스턴스를 렌더링할 수 있습니다.
// Shader
#version 300 es
layout(location = 0) in vec4 VertexPosition;
layout(location = 1) in vec4 VertexColor;
layout(location = 2) in mat4 MODELVIEWPROJECTIONMATRIX;
out vec4 Color;
void main()
{
gl_Position = MODELVIEWPROJECTIONMATRIX * VertexPosition;
Color = VertexColor;
}
셰이더에서는 이 네 개의 vec4가 자동으로 하나의 mat4로 조합됩니다.
예를 들어, 셰이더 코드에서 layout(location = 2) in mat4 MODELVIEWPROJECTIONMATRIX;로 선언하면
location = 2부터 location = 5까지의 4개의 연속된 vec4가 하나의 mat4로 매핑됩니다.
layout (location = 2) layout (location = 3) layout (location = 4) layout (location = 5)
설정한 4개의 vec4 속성은 GPU가 각 인스턴스마다 고유의 mat4로 해석하고,
이를 MODELVIEWPROJECTIONMATRIX로 사용할 수 있게 됩니다.
void Cube::RenderCube()
{
glBindBuffer( GL_ARRAY_BUFFER, matrixId );
glm::mat4* matrixBuf = (glm::mat4*)glMapBufferRange( GL_ARRAY_BUFFER, 0, sizeof(glm::mat4*)*(dimension*dimension*dimension), GL_MAP_WRITE_BIT);
static float l = 0;
TransformObj->TransformRotate(l++, 1, 1, 1);
TransformObj->TransformTranslate(-distance*dimension/4, -distance*dimension/4, -distance*dimension/4);
glm::mat4 projectionMatrix = *TransformObj->TransformGetProjectionMatrix();
glm::mat4 modelMatrix = *TransformObj->TransformGetModelMatrix();
glm::mat4 viewMatrix = *TransformObj->TransformGetViewMatrix();
int instance = 0;
LOGI("Show matrix buf size : %d", sizeof(matrixBuf));
for ( int i = 0; i < dimension; i++ )
{
for ( int j = 0; j < dimension; j++ )
{
for ( int k = 0; k < dimension; k++ )
{
matrixBuf[instance++] = projectionMatrix * viewMatrix
* glm::translate(modelMatrix,glm::vec3( i*distance , j*distance, k*distance))
* glm::rotate( modelMatrix, l, glm::vec3(1.0, 0.0, 0.0));
}
}
}
glUnmapBuffer ( GL_ARRAY_BUFFER );
glBindVertexArray(Vertex_VAO_Id);
glDrawElementsInstanced(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (void*)0, dimension*dimension*dimension);
}
인스턴스 오브젝트 1000개를 그리려면 1000개의 변환 행렬이 필요하다.
1000개의 행렬을 저장할 메모리를 glMapBufferRange를 통해 할당하자.
glDrawElementsInstanced(GL_TRIANGLES, 36, GL_UNSIGNED_SHORT, (void*)0, dimension*dimension*dimension);
glDrawElementsInstanced는 OpenGL에서 **인스턴스 렌더링(instance rendering)**을 수행하는 함수로, 하나의 드로우 호출로 여러 인스턴스를 한꺼번에 렌더링할 수 있게 해줍니다. 이 함수는 각 인스턴스가 동일한 정점 데이터를 사용하지만, 위치, 크기, 회전과 같은 변환이 인스턴스마다 다를 때 유용합니다.
매개변수 설명
- mode: 그리기 모드를 지정합니다. 렌더링할 기본 도형을 정의하는 값으로, 다음 중 하나를 사용할 수 있습니다.
- GL_TRIANGLES: 삼각형을 사용해 도형을 그림
- GL_LINES: 선을 사용해 도형을 그림
- GL_POINTS: 점을 사용해 도형을 그림
- 그 외에 GL_TRIANGLE_STRIP, GL_LINE_STRIP 등 다양한 도형이 있습니다.
- count: 각 인스턴스에서 사용할 정점 인덱스의 개수입니다.
- 예를 들어, 삼각형으로 구성된 큐브는 36개의 정점 인덱스를 사용하므로 count = 36을 지정합니다.
- type: 인덱스 배열에 있는 값의 자료형을 지정합니다. 주로 GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT 중 하나를 사용합니다.예를 들어, 인덱스 배열이 unsigned int 타입이라면 GL_UNSIGNED_INT로 설정합니다.
- indices: 인덱스 배열의 시작 위치에 대한 포인터입니다. 인덱스 버퍼 객체(IBO)에서 데이터를 사용할 경우, 0으로 설정하고 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iId);와 같이 IBO를 미리 바인딩합니다.
- instancecount: 생성할 인스턴스의 개수입니다.
예를 들어, 1000개의 큐브 인스턴스를 렌더링하고 싶다면 instancecount = 1000으로 설정합니다.
'Opengles 3.0 with Android' 카테고리의 다른 글
Chapter 3.5 Primitive Restart (2) | 2024.10.31 |
---|---|
Chapter 3.3 VAO 사용하여 Grid 렌더링 (0) | 2024.10.29 |
Chatper 3.2 Grouping Uniform and Creating Buffers (0) | 2024.10.26 |
Chapter 2.2 (Vertex Array object) 사용한 Cube 렌더링 (1) | 2024.10.22 |
Chapter 2.1 VBO(Vertex Buffer Object) 사용하여 정사각형 렌더링. 해본다. (0) | 2024.10.17 |