http://www.rastertek.com/tutdx11s2ter.html
여기에 있는 튜토리얼을 따라하도록 해야겠다. 두 번째가 Series2 라고 하는데, 둘 다 같은 내용이 있기도 하고 다른 내용을 가지기도 한다. 첫 번째 시리즈에서 Quad Tree에 대한 내용도 있어서, 그것을 알고싶기에 Series 1을 먼저 공부하고, Series 2에서 중복되는 내용이라면 넘어가도록 한다.
이 튜토리얼은 Directx11을 기반으로 하는데, 코드를 이해하면 OpenGL로 포팅할 수 있을 것이다.
Tutorial 1 : Grid and Camera Movement
이 튜토리얼은 DirectX 11을 사용하여 terrain grid를 렌더링하는 기본을 다룰 것이다. 좀 더 고급 테레인 개념에 들어가기전에, 너는 기본 grid를 그릴 수 있어야 하고, 좋은 카메라 함수를 가지고 있어야 한다. 그리고 그것은 이 튜토리얼의 목적이 될 것이다. 테레인 주위를 쉽게 움직일 수 있는 좋은 카메라와 함께, 그것은 개발하는 동안 발생할 수 있는 디버깅 문제를 도와 줄 수 있다. 좋은 디버깅 툴을 갖는 것은 항상 개발을 가속시키고, 품질을 보증하는 열쇠이다. 또한 이 튜토리얼이 DirectX 11 튜토리얼의 지식과 프레임워크를 기반으로 한다는 것에 주목해라.
이 튜토리얼에서 grid는 기본적인 100x100 unit flat terrain이 될 것이다. 그것은 TerrainClass라고 불려지는 새로운 클래스에 캡슐화될 lines로 구성될 quads로 구성될 것이다. 그 카메라는 또한 PositionClass 라고 불려지는 새로운 클래스를 기반으로 할 것이다 (CameraClass는 여전히 view matrix를 만들기 ㅜ이해 사용될 것이다, 나는 그 기능을 분리한다). PositionClass는 카메라의 위치와 회전뿐만 아니라 가속도와 deceleration을 유지할 것인데, 카메라가 테레인 주변에서 부드럽게 움직이게 하기 위해서 이다. 그리고 마지막으로 우리는 FPS, CPU usage, video card 정보, 그리고 카메라의 위치와 회전을 보여주기 위해 TextClass를 사용할 것이다.
Framework
그 frame work는 새로운 TerrainClass와 PositionClass를 포함한다. 그 클래스들의 나머지는 이미 DirectX 11 튜토리얼 섹션에서 다뤄진다.
우리는 새로운 TerrainClass를 보아서 튜토리얼을 시작할 것이다.
Terrainclass.h
그 terrain class는 model data를 캡슐화 할 것이고, 100x100 line grid를 그리는 렌더링 기능을 캡슐화할 것이다. 이 클래스는 오직 지금까지 기본적인 것만을 포함하는데, 이 튜토리얼이 처음에 매우 기본적인 terrain을 그리는데 집중할 것이기 때문이다.
{
나는 코드들을 보면서 내 엔진과 OpenGL에 쓸 수 있게 이것들을 바로바로 포팅하도록 해야겠다. 이것은 나중에 내가 DirectX를 쓸 일이 있으면 좋은 도움이 될 것 같다.
OpenGL코드는 모든 튜토리얼을 읽고 이해되고 돌아가는 코드일 때 한 번에 올리는게 좋을 듯 하다.
튜토리얼에 있는 DirectX는 내 이해를 위해 필요한 것만 직접 따라칠 것이다.
}
terrain을 위한 vertex data/structure는 position과 color가 될 것인데, 우리가 우선 하얀색 선만을 그릴것이기 때문이다.
TerrainClass는 terrain을 불러오고 releasing하고 렌더링하기위한 usual public과 private 함수들을 가진다.
Terrainclass.cpp
class constructor는 그 vertex and index buffer pointers를 null로 초기화할 것이다.
그 초기화 함수는 처음에 그 테레인의 width와 height를 설정할 것이고, 그러고나서 그 terrain model data를 유지할 vertex와 index buffers를 초기화하는 함수를 호출할 것이다.
Shutdown은 terrain model data를 가지고 있는 vertex와 index buffers를 release하기 위해 ShutdownBuffers function을 호출한다.
Render function은 terrain model data를 그래픽스 파이프라인에 넣기 위해 RenderBuffers를 호출한다. 그래서 그 ColorShaderClass object가 그것을 렌더링할 수 있다. 이것은 ApplicationClass::Render function에서 처리된다.
GetIndexCount는 terrain model에서 인덱스들의 개수를 반환한다. 이것은 테레인을 렌더링하는 쉐이더에 의해서 호출되는데, 그것들이 렌더링을 수행할 정보를 요구하기 때문이다.
InitializeBuffers 함수는 terrain grid data를 유지하는데 사용되는 vertex와 index buffers를 만든다. 그 테레인은 사각형을 만드는 직선들로 구성될 것이다. DirectX 11에서 선을 그리기 위해 너는 두 개의 점이 필요하고, 사각형을 그리기 위해서, 그 4개의 선을 형성할 8개의 점이 필요하다. 그래서 아래의 코드에서, 너는 4개의 선을 만들기 위해 8개 점을 사용하는 100x100 grid에서 각 square를 만드는 for문을 볼 수 있을 것이다. 우리는 model을 불러오는 것이 아니기 때문에, 나는 terrain grid를 만들기 위해 vertex and index array를 사용하고, 그러고나서 그러한 배열들로 부터 vertex and index buffer를 만든다.
100 * 100 mesh에서 정점들의 개수를 결정한다.
m_vertexCount = (m_terrainWidth - 1) * (m_terrainHeight - 1) * 8;
m_indexCount = m_vertexCount;
임의의 vertex와 index 배열들을 만든다.
이제 100x100 terrain grid를 형성할 line lists를 가진 vertex와 index 배열들을 불러온다.
// Initialize the index to the vertex array index = 0; // Load the vertex and index arrays with the terrain data for (int j = 0; j < (m_terrainHeight - 1); ++j) { for (int i = 0; i < (m_terrainWidth - 1); ++i) { // LINE 1 // Upper Left positionX = (float)i; positionZ = (float)(j + 1); vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // Upper right positionX = (float)(i + 1); positionZ = (float)(j + 1); vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // LINE 2 // Upper right positionX = (float)(i + 1); positionZ = (float)(j + 1); vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // Bottom right positionX = (float)(i + 1); positionZ = (float)j; vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // LINE 3 // Bottom right positionX = (float)(i + 1); positionZ = (float)j; vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // Bottom left positionX = (float)i; positionZ = (float)j; vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // LINE 4 // Bottom left positionX = (float)i; positionZ = (float)j; vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; // Upper Left positionX = (float)i; positionZ = (float)(j + 1); vertices[index].position = D3DXVECTOR3(positionX, 0.f, positionZ); vertices[index].color = D3DXVECTOR4(1.f, 1.f, 1.f, 1.f); indices[index] = index; ++index; } }
vertex and index 배열로부터 vertex와 index buffers를 만든다.
마지막으로, 그 데이터가 버퍼에 있기 때문에 그 배열을 해제한다.
그 ShutdownBuffers 함수는 terrain data를 유지하기 위해 사용된 그 vertex and index buffer를 해제한다.
RenderBuffers는 렌더링을 위한 그래픽스 파이프라인에 line list terrain model을 배치시킨다.
render format을 line list로 설정한다.
Positionclass.h
PositionClass는 camera/viewer location과 camera movement functionality를 캡슐화하는 클래스이다.
PositionClass는 viewer/camera의 위치와 회전을 설정하고 가져오는 helper function을 가진다.
setFrameTime은 프로그램의 속도와 viewer/camera의 싱크를 맞추기 위해 사용된다.
movement 함수들은 사용자의 입력을 기반으로 viewer/camera를 움직이기 위해 호출된다.
Positionclass.cpp
클래스 생성자는 모든 위치, 회전, 프레임 시간, 속도 변수들을 zero 초기화한다.
SetPosition과 SetRotation 함수는 viewer/camera의 위치와 회전을 설정하는데 사용된다. 이러한 함수들은 일반적으로 원점보다는 카메라의 위치를 초기화하는데 사용된다. 이 튜토리얼에서 그 카메라는 grid 뒤와 그것의 중심에 있도록 설정된다.
GetPosition과 GetRotation 함수들은 카메라 위치에서의 현재 position과 회전을 반환한다. 이 튜토리얼에서, 이러한 함수들은 display 목적으로 카메라의 위치와 회전을 제공하기 위해 호출된다. 우리는 스크린의 왼쪽 면에 text strings으로서 position과 rotation을 그릴 것이다. 이것은 디버깅에 유용하다.
SetFrameTime 함수는 매 프레임마다 호출될 필요가 있다. 그것은 private 멤버 변수안에 현재 프레임 시간을 저장하고, movement calculation functions에 의해 사용된다. 이 방식으로, 프로그램이 작동하는 속도와 상관없이, 움직임과 회전 속도는 같게 된다. 만약 이것이 되어지지 않는다면, 그 움직이는 비율은 프레임율에 따라 가속되거나 느려질 것이다.
다음의 8개 움직임 함숟르은 거의 똑같이 작동한다. 모든 8개의 함수들은 매 프레임마다 호출된다. 각 함수에 대해 키가 눌려지는 인풋 변수는 사용자가 forward key, backward key, 그리고 등등을 누르고 있는지를 알려준다. 만약 사용자가 그 키를 누르고 있다면, 그러면 매 플에ㅣㅁ마다 그 스피드는 그것이 maximum이 될 때 까지 가속할 것이다. 이 방식으로 카메라는 부드러운 움직임과 높은 반응성을 만드는 차량에서의 가속도 유사한 것을 만들어낸다. 마찬가지로, 만약 사용자가 키를 누르는 걸 멈추고, keydown variable이 flase라면, 그것은 그러고나서 부드럽게 speed가 zero가 될 때 까지 매 프레임마다 느려질 것이다. 그 속도는 움직임 속도가 프레임율과 상관없도록 하기 위해 frame time에 대해 계산된다. 각 함수는 그러고나서 viwer/camera의 새로운 위치를 계산하기 위해 기본 수학을 사용한다.
이 함수는 viewer/camera의 forward speed 와 movement를 계산한다.
이 함수는 viewer/camera의 backward speed 와 movement를 계산한다.
이 함수는 viewer/camera의 upward speed 와 movement를 계산한다.
이 함수는 viewer/camera의 left turn speed 와 rotation를 계산한다.
이 함수는 viewer/camera의 right turn speed 와 rotation를 계산한다.
이 함수는 viewer/camera의 upward turn speed 와 rotation를 계산한다.
이 함수는 viewer/camera의 downward turn speed 와 rotation를 계산한다.
Systemclass.h
SystemClass는 DirectX tutiroals version에서 조금 수정되었다. 이제 그것은 Windows initialization과 new ApplicationClass를 포함한다.
우리는 새로운 ApplicationClass에 대한 헤더를 여기에서 include한다.
우리는 또한 새로운 ApplicationClass object에 대한 private pointer를 포함한다.
Systemclass.cpp
새로운 ApplicationClass object pointer는 class constructor에서 null로 초기화된다.
그 ApplicationClass object가 만들어지고 여기에서 초기화된다.
새로운 ApplicationClass object는 Shutdown function에서 해제된다.
frame processing은 이제 ApplicationClass를 호출하는데, all application processing이 그것안에 캡슐화될 수 있게 하기 위해서이다. 이것은 SystemClass의 목적을 간단하게 하는데 도움을 준다.
Application.h
ApplicationClass는 전체 terrain application의 main wrapper class이다. 그것은 모든 graphics, input, and processing을 다룬다.
Application.cpp
class constructor는 모든 오브젝트 포인터들을 null로 초기화한다.
input object를 만들고 초기화한다.
DirectX 11 object를 만들고 초기화한다.
camera object를 만들고 초기화한다.
terrain object를 만들고 초기화한다.
color shader object를 만들고 초기화한다.
timer object를 만들고 초기화한다.
position object를 만들고 초기화한다.
FPS object를 만들고 초기화한다.
CPU 사용 오브젝트를 만들고 초기화한다.
font shader object를 만들고 초기화한다.
text object를 만들고 초기화한다.
Shutdown function은 Initialize function에서 만들어진 모든 오브젝트들을 해제한다.
Frame function은 terrain application에 대한 모든 처리를 한다. 모든 처리, input, 그리고 그래픽스는 여기에서 처리된다.
클래스 구조에 관한 내용이므로 여기까지 번역하도록 한다.
=================================================
grid만드는 건 쉬웠다. 아래는 OpenGL 코드로 내 엔진에 맞게 작성한 것이다.
#include <Terrain/CGTerrain.h> CGProj::CGTerrain::CGTerrain() : m_terrainWidth(100), m_terrainHeight(100), // Manual Setting m_vertexCount(0), m_indexCount(0), m_VAO(0), m_VBO(0) { } void CGProj::CGTerrain::initialize(CGAssetManager & am) { m_vertexCount = (m_terrainWidth - 1) * (m_terrainHeight - 1) * 8; m_indexCount = m_vertexCount; struct terrainData { glm::vec3 position; glm::vec4 color; }; terrainData* vertices = new terrainData[m_vertexCount]; unsigned* indices = new unsigned[m_indexCount]; int positionX = 0; int positionZ = 0; int index = 0; for (unsigned j = 0; j < m_terrainHeight - 1; ++j) { for (unsigned i = 0; i < m_terrainWidth - 1; ++i) { // LINE 1 // Upper left. positionX = (float)i; positionZ = (float)(j + 1); vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // Upper right. positionX = (float)(i + 1); positionZ = (float)(j + 1); vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // LINE 2 // Upper right. positionX = (float)(i + 1); positionZ = (float)(j + 1); vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // Bottom right. positionX = (float)(i + 1); positionZ = (float)j; vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // LINE 3 // Bottom right. positionX = (float)(i + 1); positionZ = (float)j; vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // Bottom left. positionX = (float)i; positionZ = (float)j; vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // LINE 4 // Bottom left. positionX = (float)i; positionZ = (float)j; vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; // Upper left. positionX = (float)i; positionZ = (float)(j + 1); vertices[index].position = glm::vec3(positionX, 0.0f, positionZ); vertices[index].color = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f); indices[index] = index; index++; } } glGenVertexArrays(1, &m_VAO); glGenBuffers(1, &m_VBO); glGenBuffers(1, &m_EBO); glBindVertexArray(m_VAO); glBindBuffer(GL_ARRAY_BUFFER, m_VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(terrainData) * m_vertexCount, &vertices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned) * index, &indices[0], GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 7, (void*)0); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(float) * 7, (void*)(sizeof(float) * 3)); glBindVertexArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); delete[] vertices; delete[] indices; m_lineShader = am.getShader(SHADER_CG_LINE); } void CGProj::CGTerrain::render(const glm::mat4 & view, const glm::mat4 & proj) { m_lineShader->use(); m_lineShader->setMat4("view", view); m_lineShader->setMat4("projection", proj); glBindVertexArray(m_VAO); glDrawElements(GL_LINES, m_indexCount, GL_UNSIGNED_INT, 0); glBindVertexArray(0); }
댓글 없음:
댓글 쓰기