필요한 개념
Instancing(인스턴싱) : Mesh나 Model을 대량으로 그려낼 수 있는 기술(VertexBuffer(틀하나를 가지고)로 와장창 찍어낸다 해서 인스턴스)
InstancingDemo.h 를 실행시켜보면 프레임수가 상당히 낮다(하나씩 각각 그리기 때문에)
DpCall(Draw Primitive Call) : 렌더링 파이프라인을 한번 거치는 과정, 데이터를 세팅하고 모든 과정을 거쳐서
렌더링까지 가야 하므로 소요비용이 상당함, 게임에서는 최적화라고 하면 이것부터 줄이는 작업이 시작됨
DpCall을 1000번을 그리는데 DpCall을 1000번 씀
인스턴싱은 DpCall을 1000번을 그리는데 DpCall을 1번 씀, 이 상황에서는 (1/1000)로 줄어든다.
-> 그래서 세팅이 한번 일어나고, 그리는게 1번으로 바뀜(DpCall) -> 속도가 달라짐
현재 인스턴싱을 쓰지 않는 게임은 거의 없다.
VertexBuffer가 정점 정보를 가지고 있다.
인스턴싱은 하나가 더 들어감(VertexBuffer로)
-> 즉, VertexBuffer로 instanceBuffer가 하나더 생김
Buffer 슬롯 세팅
0 -> VertexBuffer
1 -> InstanceBuffer
instanceBuffer는 world 정보들을 가지고 있다.
->
VertexBuffer에는 정점정보가 들어가 있고
instanceBuffer(VertexBuffer로 만든)에는 SRT(world) 정보들이 들어가게 된다.
만약 1000개를 그리고 싶다면
InstanceBuffer에 1000개의 SRT(world)정보가 들어간다.
-> 즉 instance 배열 크기만큼 그려내는 것이다.
결국 Dp에서는 이것을 한번만 그리는 것처럼 보이지만 내부적으로는 갈라서 그리는 것이다.
기존 보다 속도가 월등히 올라감!
내 기준 기존 500프레임 -> 4000프레임으로 올라감
작업관리자 켜보면 -> GPU 엔진에 0번 1번이 매겨져 있다.
노트북에서는
0번 : 내장 그래픽
1번 : 외장 그래픽
교수님 PC에서는
0번 : 외장 그래픽
1번 : 내장 그래픽
내장 그래픽을 사용하면 프레임이 더 떨어져서 나온다.
-> 그래서 외장 그래픽으로 사용하도록 하는 코드로 바꿔준다.(D3D.h 관련)
또는 그래픽 카드 설정 이용해서 강제로 외장 그래픽카드로 작동하게 할 수 있다.
InstancingDemo.h
#pragma once
#include "Systems/IExecute.h"
class InstancingDemo : public IExecute
{
public:
virtual void Initialize() override;
virtual void Ready() override {};
virtual void Destroy() override {};
virtual void Update() override;
virtual void PreRender() override {};
virtual void Render() override;
virtual void PostRender() override {};
virtual void ResizeScreen() override {};
private:
void CreateMesh();
private:
Shader* shader;
Material* material;
vector<Mesh::MeshVertex> vertices;
vector<UINT> indices;
VertexBuffer* vertexBuffer; // 0번 슬롯
VertexBuffer* instanceBuffer; // 1번 슬롯
IndexBuffer* indexBuffer;
PerFrame* perFrame;
Transform* transforms[1000];
Matrix worlds[1000];
};
InstancingDemo.Cpp
#include "stdafx.h"
#include "InstancingDemo.h"
void InstancingDemo::Initialize()
{
Context::Get()->GetCamera()->RotationDegree(0, 0, 0);
Context::Get()->GetCamera()->Position(0, 0, -17);
shader = new Shader(L"50_Instancing.fx");
// 프레임 별로 밀어줄려고
perFrame = new PerFrame(shader);
// DiffuseMap 사용할려고
material = new Material(shader);
material->DiffuseMap(L"Box.png");
for (UINT i = 0; i < 1000; i++)
{
//transforms[i] = new Transform(shader);
transforms[i] = new Transform();
transforms[i]->Position(Math::RandomVec3(-30, 30));
transforms[i]->Scale(Math::RandomVec3(1.0f, 2.5f));
transforms[i]->RotationDegree(Math::RandomVec3(-180, 180));
// worlds 세팅
worlds[i] = transforms[i]->World();
}
CreateMesh();
// instance는 1번 슬롯을 사용한다.
instanceBuffer = new VertexBuffer(worlds, 1000, sizeof(Matrix), 1);
}
void InstancingDemo::Update()
{
perFrame->Update();
// shader로 넘어갈게 아니여서 필요없다.(instanceBuffer가 넘어감)
/*for (UINT i = 0; i < 1000; i++)
transforms[i]->Update();*/
}
void InstancingDemo::Render()
{
perFrame->Render();
material->Render();
vertexBuffer->Render();
instanceBuffer->Render();
indexBuffer->Render();
D3D::GetDC()->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
//for (UINT i = 0; i < 1000; i++)
//{
// transforms[i]->Render();
// shader->DrawIndexed(0, 0, indices.size());
//}
//DpCall은 한번만 들어가면 된다.
// 3번인자 : 인스턴스 하나당 인덱스의 개수
// 4번인자 : 그릴려는 인스턴싱의 개수
shader->DrawIndexedInstanced(0, 0, indices.size(), 1000);
}
void InstancingDemo::CreateMesh()
{
float w, h, d;
w = h = d = 0.5f;
//Front
vertices.push_back(Mesh::MeshVertex(-w, -h, -d, 0, 1, 0, 0, -1));
vertices.push_back(Mesh::MeshVertex(-w, +h, -d, 0, 0, 0, 0, -1));
vertices.push_back(Mesh::MeshVertex(+w, +h, -d, 1, 0, 0, 0, -1));
vertices.push_back(Mesh::MeshVertex(+w, -h, -d, 1, 1, 0, 0, -1));
//Back
vertices.push_back(Mesh::MeshVertex(-w, -h, +d, 1, 1, 0, 0, 1));
vertices.push_back(Mesh::MeshVertex(+w, -h, +d, 0, 1, 0, 0, 1));
vertices.push_back(Mesh::MeshVertex(+w, +h, +d, 0, 0, 0, 0, 1));
vertices.push_back(Mesh::MeshVertex(-w, +h, +d, 1, 0, 0, 0, 1));
//Top
vertices.push_back(Mesh::MeshVertex(-w, +h, -d, 0, 1, 0, 1, 0));
vertices.push_back(Mesh::MeshVertex(-w, +h, +d, 0, 0, 0, 1, 0));
vertices.push_back(Mesh::MeshVertex(+w, +h, +d, 1, 0, 0, 1, 0));
vertices.push_back(Mesh::MeshVertex(+w, +h, -d, 1, 1, 0, 1, 0));
//Bottom
vertices.push_back(Mesh::MeshVertex(-w, -h, -d, 1, 1, 0, -1, 0));
vertices.push_back(Mesh::MeshVertex(+w, -h, -d, 0, 1, 0, -1, 0));
vertices.push_back(Mesh::MeshVertex(+w, -h, +d, 0, 0, 0, -1, 0));
vertices.push_back(Mesh::MeshVertex(-w, -h, +d, 1, 0, 0, -1, 0));
//Left
vertices.push_back(Mesh::MeshVertex(-w, -h, +d, 0, 1, -1, 0, 0));
vertices.push_back(Mesh::MeshVertex(-w, +h, +d, 0, 0, -1, 0, 0));
vertices.push_back(Mesh::MeshVertex(-w, +h, -d, 1, 0, -1, 0, 0));
vertices.push_back(Mesh::MeshVertex(-w, -h, -d, 1, 1, -1, 0, 0));
//Right
vertices.push_back(Mesh::MeshVertex(+w, -h, -d, 0, 1, 1, 0, 0));
vertices.push_back(Mesh::MeshVertex(+w, +h, -d, 0, 0, 1, 0, 0));
vertices.push_back(Mesh::MeshVertex(+w, +h, +d, 1, 0, 1, 0, 0));
vertices.push_back(Mesh::MeshVertex(+w, -h, +d, 1, 1, 1, 0, 0));
indices =
{
0, 1, 2, 0, 2, 3,
4, 5, 6, 4, 6, 7,
8, 9, 10, 8, 10, 11,
12, 13, 14, 12, 14, 15,
16, 17, 18, 16, 18, 19,
20, 21, 22, 20, 22, 23
};
vertexBuffer = new VertexBuffer(&vertices[0], vertices.size(), sizeof(Mesh::MeshVertex));
indexBuffer = new IndexBuffer(&indices[0], indices.size());
}
Instancing.fx
#include "00_Global.fx"
float3 Direction = float3(-1, -1, +1);
struct VertexInput
{
// 0번 슬롯
float4 Position : Position;
float2 Uv : Uv;
float3 Normal : Normal;
// 1번 슬롯
// Inst가 붙은 애들을 만든 Shader 클래스에서 1번으로 취급(이후에 설명)
// world 정보가 들어가 있다.
matrix Transform : Inst1;
};
struct VertexOutput
{
float4 Position : SV_Position;
float2 Uv : Uv;
float3 Normal : Normal;
};
VertexOutput VS(VertexInput input)
{
VertexOutput output;
// 한번에 Transform에 1000개의 matrix가 들어온다.
// 1번 버퍼에 의해 계속 콜될때 마다 0, 1, 2, 3.... 바뀜
World = input.Transform;
output.Position = WorldPosition(input.Position);
output.Position = ViewProjection(output.Position);
output.Normal = WorldNormal(input.Normal);
output.Uv = input.Uv;
return output;
}
float4 PS(VertexOutput input) : SV_Target
{
float3 normal = normalize(input.Normal);
float3 light = -Direction;
return DiffuseMap.Sample(LinearSampler, input.Uv) * dot(light, normal);
}
technique11 T0
{
P_VP(P0, VS, PS)
P_RS_VP(P1, FillMode_WireFrame, VS, PS)
}
결과
Instancing을 사용하니 프레임이 4000가까이 나오는 것을 볼 수 있다.
(이전에는 각각 그려서(1000개) 기준 500 프레임이었다.)
'DirectX11 3D > 기본 문법' 카테고리의 다른 글
<DirectX11 3D> 52 - Instancing(Model) (0) | 2022.02.22 |
---|---|
<DirectX11 3D> 51 - Instancing(Mesh) (0) | 2022.02.22 |
<DirectX11 3D> 49 - Animation(Blending) (0) | 2022.02.21 |
<DirectX11 3D> 48 - Animation(FrameLerp, Tween) (0) | 2022.02.21 |
<Directx11 3D> 46 - Animation Rendering (0) | 2022.02.17 |