본문 바로가기

DirectX11 3D/기본 문법

<DirectX11 3D> 96 - GS(Geometry Shader)

 

필요한 개념


그림 8-1

 

VS, PS -> DX9에서 추가됨
GeometryShader -> DX10에서 추가됨

GeometryShader를 이용해서 빌보드 업그레이드 가능

DX10 : GeometryShader, StreamOutput 추가
GeometryShader : 면을 다루는 셰이더
StreamOutput : 3D 셰이더의 결과를 리턴

VS는 정점하나당 Shader가 콜이 되었다고 하면
GS는 면 하나당 Shader가 콜이 된다.

VS를 통해 정점 3개 해서 삼각형을 모으면
삼각형 하나가 GS 하나를 콜함
(사각형 단위에서도 콜 가능하지만, 일반적으로는 삼각형단위에서 콜함)


새롭게 기능이 추가되면 이전꺼는 거의 사용하지 않는다.(다 3D에서만 추가된 기능들이다.)
VS에서 할 역할을 GS에서도 할 수 있다.(VS에서는 거의 값을 패스하거나, 약간의 값 수정만 할 뿐이다.)
-> GS를 대신 사용하면 퍼포먼스가 빠른 경우가 많다.

그래서 신 버젼의 셰이더를 사용하게 되면 기존 버젼의 셰이더는 사용하지 않는다.

SO(StreamOutput) : RS에서 처리되기 전에 VS, GS에서 처리된 결과를 C쪽으로 리턴해주는 역할이다.
ex) 물결 같은 것을 만든다고 하면 VS, GS에서 만들어 내고, 물결이 높이가 Shader에서 바뀌었으니까, C쪽에 배 모델이 있다고 한다면 높이를 얻어와
배 높이를 조정할 수 있다. 그런 것들이 전부다 SO통해서 리턴을 받아와서 사용
옛날에 10에서는 그렇게 했는데, 11에서는 이 기능 잘 안씀
이유는 물결만들고 하는 것들을, VS, GS 통하는 것이 아니라, CS로 계산함, 그래서 지금은 SO를 쓰지 않는다.

GS는 면을 다루는 것이다.

빌보드를 수정하면서 진행한다.


이제까지는 정점4개와 Index 6개를 넘겼다. GS를 이용하면 정점하나로 면을 만들 수 있다.(->메모리 확줌, 정점 하나면됨, 인덱스도 필요없음)
Vertex를 3, 4개씩 넘겨서 면을 만들 수 있지만, 일반적으로는 GS를 이용해 점하나를 펼치는 방식을 사용한다.(GS를 이용해 정점하나로 면을 만든다.)
훨씬 더 메모리가 효율적으로 절약됨

Uv도 지오메트리에서 전개함

GS를 이용할 거여서
정점 하나씩 넘겨서 다루기 때문에 PointList를 사용(정점하나당 VertexShader하나가 붙어서 처리됨), 정점하나단위로 넘김
D3D11_PRIMITIVE_TOPOLOGY_POINTLIST

이제까지는(TRIANGLELIST) 정점 3개가 Shader에 한번에 몰려서 call이 된다.(3개씩 묶은것을 여러개를 동시에 콜)

이제는 C보다 Shader 코드가 길어진다.

World 변환, ViewProjection 변환은 지오메트리 셰이더에서 수행

geometry에서는 triangleStrip으로 처리한다.
그래서 사각형이 나옴

Maxvertexcount? 지오메트리에서 출력될 최대 개수(정점을 몇개를 내보낼 거냐)
append할때 이거 넘어가면 정상적으로 렌더링 되지 않거나, 터진다.
return은 void이다.
정점을 하나씩 받으면 point고, 정점을 세개씩 받으면 triangle이 붙음
triangle 사용시에는 triangle VertexOutput input[3]가 된다.(3개의 배열이 붙음)
지오메트리 셰이더는 리턴 방식이 아닌 TriangleStream에 정점을 추가해주는
방식으로 처리
지오메트리는 무조건 삼각형 단위여서 TriangleStream이 붙음
<에는 OutputType이 들어감> dx10부터는 <> template이 생겼다.

 

그림 9-1


TriangleList - 3개씩 끊어서 삼각형 단위 렌더링
TriangleStrip - 3개로 삼각형을 만든 후, 정점을 연결해서 삼각형을 그림
TriangleFan - 중심점을 기준으로 계속 찍어가며 삼각형을 그림

월드를 GS에서 변환해도 되지만, 정점을 펼친상태에서 하니깐 월드 변환이
4번일어난다. VS에선 한번에 할 수 있다.(VS에서는 점단위로 들어오니깐)

RS이전에 이제 VS가 아니라 GS여서 GS에 SV_Position이 붙음, SV_Position이 RasterizerState 처리 (PS에서 처리하지 않음)
VS에서 붙인다고 안되는건 아니지만, 정석상 이게 맞음


요약
maxvertexcount : 출력할 최대 정점 개수
append : 렌더링 할 정점 추가(Triangle_Strip)
VS->GS->RS이므로 GS 출력 자료형(GeometryOutput)에 SV_Position이 붙음

 

그림 10-1(Cross 설명)

 

 


Cross : GS 회전시켜서 크로스 만드는거

빌보드 효과를 없애려고 크로스 스타일로도 씀
정점을 8개를 내보낸다.
정점 하나로 면을 두개 생성하므로 maxvertexcount가 8개가 된다.

RestartStrip이란? 스트립을 다시 시작해서 추가(끊어라, 스트립을 다시 시작하라)
다시 0, 1, 2, 3이 시작됨

RasterizerState CullMode = None; 을 주면 아예 잘라내지 마라

결과를 보면 어느쪽으로 봐도 십자가 모양이 성립을 한다.
그나마 면처럼 안보임

일반적으로 십자가 보다 면을 2개 더 추가해서(총 4개면) 대각선 까지해서 하는 것을 많이 쓴다.

텍스쳐가 한종류니까 너무 심심해 보임(실제 텍스쳐를 깔때 여려종류를 깐다.)
텍스쳐 여러개 쓰고 싶다면 Texture2DArray가 있다.(한번 렌더링때 텍스쳐 어려개를 넣을 수 있다.)

BillBoard객체를 늘리는 것은 현명한 방법이 못된다. 늘리면 DPCall은 늘어나고 -> Rendering 속도는 떨어진다.
그래서 텍스쳐를 여러장을 shader에 보낸다.(텍스쳐 배열로 넘김(Texture2DArray))

그냥 Texture2D BillboardMap[100]는 안된다.
왜냐하면
int a = 0;
BillboardMap[a].Sample()
[]안에는 상수만 들어갈 수 있도록 세팅되어있다.(변수는 못들어감)
-> 그래서 Texture2DArray를 사용해야 한다.

Texture2DArray는 Sample()시에 float3(z가 인덱스값)가 들어감
Texture2DArray BillboardMap;
int a = 0;
BillboardMap.Sample(LinearSampler, float3(input.Uv, a));
이렇게 가능하다.

Texture2DArray를 이용해서 여러 텍스쳐를 렌더링 할 것이다.

해당 정점은 몇번째 텍스쳐로 렌더링 되어야하는지 모르니깐, 알수없어서 VertexBillboard,Output들에 uint MapIndex : MapIndex 변수 추가
mapIndex -> 정점이 몇번째 텍스쳐로 렌더링 될꺼냐

만약 Shader에서 지금 들어오는 정점이 몇번 정점인지 알고 싶다면?
uint VertexIndex : SV_VertexID;를 쓰면 된다.
SV_VertexID에 시스템에서 해당 정점에 인덱스 값을 준다.

결과를 보니 잘나온다.
그런데 깊이 때문에 텍스쳐가 곂쳐서 나오는 부분도 있다.
-> 파티클 처럼 AlphaBlend의 알파 겹침 문제로 렌더링이 되지 않는 부분들이 생긴다. DepthRead_Particle로 해도 되지만, 간단히 해결할려면 AlphaToCoverage를 켜면 외곽선을 정리하기 때문에
알파 겹침 문제가 어느정도 해결된다.
파티클은 외곽선이 불분명해서 DepthRead_Particle로 한것이고, 이 풀 텍스쳐는 외곽선이 분명해서 AlphaToCoverage를 키면 된다.

 

 

 

 

Billboard.h 수정됨(GS에 맞도록)


더보기
#pragma once


// 최대 빌보드 개수(면 최대 개수)
#define MAX_BILLBOARD_COUNT 10000

class Billborad : public Renderer
{
public:
	Billborad(Shader* shader);
	~Billborad();

	void Update();
	void Render();

	// 외부에서 위치 추가할 수 있도록
	void Add(Vector3& position, Vector2& scale, UINT mapIndex);
	void AddTexture(wstring file);

private:
	struct VertexBillboard
	{
		Vector3 Position;
		Vector2 Scale;
		UINT mapIndex;
	};

private:
	vector<VertexBillboard> vertices;

	vector<wstring> textureNames;
	TextureArray* textureArray = NULL;
	ID3DX11EffectShaderResourceVariable* sDiffuseMap;
};

 

 

 

 

 

 

 

Billboard.cpp 수정됨(GS에 맞도록)


더보기
#include "Framework.h"
#include "Billboard.h"

Billborad::Billborad(Shader* shader) :
	Renderer(shader)
{
	// GS를 이용할 거여서
	// 정점 하나씩 넘겨서 다루기 때문에 PointList를 사용
	Topology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST);
	sDiffuseMap = shader->AsSRV("BillboardMap");
}

Billborad::~Billborad()
{
	SafeDelete(textureArray);
}

void Billborad::Update()
{
	Super::Update();
}

void Billborad::Render()
{
	// textureArray개수가 바뀌면 생성을 다시 해줘야한다.
	if (textureNames.size() > 0 && textureArray == NULL)
	{
		SafeDelete(textureArray);

		// textureArray 개수에 따라 새로 갯수만큼 이미지 만들어줌
		textureArray = new TextureArray(textureNames);
	}


	// vertexCount = 0, 정점개수가 다르다면
	if (vertexCount != vertices.size())
	{
		vertexCount = vertices.size();

		// 지웠다가 다시생성
		SafeDelete(vertexBuffer);
		vertexBuffer = new VertexBuffer(&vertices[0], vertices.size(), sizeof(VertexBillboard));
		
		// 위에 방식대로 해도 되고, 기존 MAP 방식으로 해도 되고
		// 대신 Map일시에는 CpuWrite가 켜져야 한다.
	}

	Super::Render();

	sDiffuseMap->SetResource(textureArray->SRV());
	shader->Draw(0, Pass(), vertexCount);
}

void Billborad::AddTexture(wstring file)
{
	textureNames.push_back(file);
}

void Billborad::Add(Vector3& position, Vector2& scale, UINT mapIndex)
{
	VertexBillboard vertex =
	{
		position, scale, mapIndex
	};

	vertices.push_back(vertex);
	// push_back은 복사생성자를 콜함
	// emplace_back 이 더 효율적(emplace_back은 이동생성자를 콜함)
	// 복사가 일어나지 않아서 더 빠르고 메모리가 효율적
}

 

 

BillboardDemo.h


더보기
#pragma once
#include "Systems/IExecute.h"

class BillboardDemo : 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 Billboards();
	void Mesh();
	void Airplane();
	void Kachujin();
	void KachujinCollider();
	void KachujinWeapon();
	void PointLighting();
	void SpotLighting();

	void Pass(UINT mesh, UINT model, UINT anim);

private:
	Shader* shader;

	Billborad* billboard;

	CubeSky* sky;

	Material* floor;
	Material* stone;
	Material* brick;
	Material* wall;

	MeshRender* cube;
	MeshRender* cylinder;
	MeshRender* sphere;
	MeshRender* grid;

	ModelRender* airplane = NULL;


	ModelAnimator* kachujin = NULL;
	Transform* colliderInitTransforms;
	ColliderObject** colliders;

	ModelRender* weapon = NULL;
	Transform* weaponInitTransform;

	vector<MeshRender*> meshes;
	vector<ModelRender*> models;
	vector<ModelAnimator*> animators;
};

 

 

 

 

 

 

 

BillboardDemo.cpp


더보기
#include "stdafx.h"
#include "BillboardDemo.h"

void BillboardDemo::Initialize()
{
	Context::Get()->GetCamera()->RotationDegree(20, 0, 0);
	Context::Get()->GetCamera()->Position(1, 36, -85);

	shader = new Shader(L"96_Billboard.fxo");

	sky = new CubeSky(L"Environment/GrassCube1024.dds");

	Billboards();

	Mesh();
	Airplane();

	Kachujin();
	KachujinCollider();
	KachujinWeapon();

	PointLighting();
	SpotLighting();
}

void BillboardDemo::Update()
{
	sky->Update();

	cube->Update();
	grid->Update();
	cylinder->Update();
	sphere->Update();

	airplane->Update();
	kachujin->Update();

	Matrix worlds[MAX_MODEL_TRANSFORMS];
	for (UINT i = 0; i < kachujin->GetTransformCount(); i++)
	{
		kachujin->GetAttachTransform(i, worlds);

		weapon->GetTransform(i)->World(weaponInitTransform->World() * worlds[40]);
	}

	weapon->UpdateTransforms();
	weapon->Update();

	billboard->Update();
}

void BillboardDemo::Render()
{
	sky->Render();


	Pass(0, 1, 2);

	wall->Render();
	sphere->Render();

	brick->Render();
	cylinder->Render();

	stone->Render();
	cube->Render();

	floor->Render();
	grid->Render();

	airplane->Render();

	kachujin->Render();
	weapon->Render();

	billboard->Render();
}

void BillboardDemo::Billboards()
{
	billboard = new Billborad(shader);
	billboard->Pass(4);
	billboard->AddTexture(L"Terrain/grass_14.tga");
	billboard->AddTexture(L"Terrain/grass_07.tga");
	billboard->AddTexture(L"Terrain/grass_11.tga");
	
	for (UINT i = 0; i < 1200; i++)
	{
		Vector2 scale = Math::RandomVec2(1, 3);
		// 5(밑에 판 크기가 -5 ~ 5 -> 10) * 12(grid 크기) = 60
		Vector2 position = Math::RandomVec2(-60, 60);

		billboard->Add(Vector3(position.x, scale.y * 0.5f, position.y), scale, 0);
	}

	for (UINT i = 0; i < 300; i++)
	{
		Vector2 scale = Math::RandomVec2(1, 3);
		Vector2 position = Math::RandomVec2(-60, 60);

		billboard->Add(Vector3(position.x, scale.y * 0.5f, position.y), scale, 1);
	}

	for (UINT i = 0; i < 700; i++)
	{
		Vector2 scale = Math::RandomVec2(1, 3);
		Vector2 position = Math::RandomVec2(-60, 60);

		billboard->Add(Vector3(position.x, scale.y * 0.5f, position.y), scale, 2);
	}

}

void BillboardDemo::Mesh()
{
	//Create Material
	{
		floor = new Material(shader);
		floor->DiffuseMap("Floor.png");
		floor->Specular(1, 1, 1, 20);
		floor->SpecularMap("Floor_Specular.png");
		floor->NormalMap("Floor_Normal.png");

		stone = new Material(shader);
		stone->DiffuseMap("Stones.png");
		stone->Specular(1, 1, 1, 20);
		stone->SpecularMap("Stones_Specular.png");
		stone->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		stone->NormalMap("Stones_Normal.png");


		brick = new Material(shader);
		brick->DiffuseMap("Bricks.png");
		brick->Specular(1, 0.3f, 0.3f, 20);
		brick->SpecularMap("Bricks_Specular.png");
		brick->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		brick->NormalMap("Bricks_Normal.png");

		wall = new Material(shader);
		wall->DiffuseMap("Wall.png");
		wall->Specular(1, 1, 1, 20);
		wall->SpecularMap("Wall_Specular.png");
		wall->Emissive(1.0f, 1.0f, 1.0f, 0.3f);
		wall->NormalMap("Wall_Normal.png");
	}

	//Create Mesh
	{
		Transform* transform = NULL;

		cube = new MeshRender(shader, new MeshCube());
		transform = cube->AddTransform();
		transform->Position(0, 5, 0);
		transform->Scale(20, 10, 20);

		grid = new MeshRender(shader, new MeshGrid(5, 5));
		transform = grid->AddTransform();
		transform->Position(0, 0, 0);
		transform->Scale(12, 1, 12);

		cylinder = new MeshRender(shader, new MeshCylinder(0.5f, 3.0f, 20, 20));
		sphere = new MeshRender(shader, new MeshSphere(0.5f, 20, 20));
		for (UINT i = 0; i < 5; i++)
		{
			transform = cylinder->AddTransform();
			transform->Position(-30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = cylinder->AddTransform();
			transform->Position(30, 6, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);


			transform = sphere->AddTransform();
			transform->Position(-30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);

			transform = sphere->AddTransform();
			transform->Position(30, 15.5f, -15.0f + (float)i * 15.0f);
			transform->Scale(5, 5, 5);
		}
	}

	sphere->UpdateTransforms();
	cylinder->UpdateTransforms();
	cube->UpdateTransforms();
	grid->UpdateTransforms();

	meshes.push_back(sphere);
	meshes.push_back(cylinder);
	meshes.push_back(cube);
	meshes.push_back(grid);
}

void BillboardDemo::Airplane()
{
	airplane = new ModelRender(shader);
	airplane->ReadMesh(L"B787/Airplane");
	airplane->ReadMaterial(L"B787/Airplane");

	Transform* transform = airplane->AddTransform();
	transform->Position(2.0f, 9.91f, 2.0f);
	transform->Scale(0.004f, 0.004f, 0.004f);
	airplane->UpdateTransforms();

	models.push_back(airplane);
}

void BillboardDemo::Kachujin()
{
	kachujin = new ModelAnimator(shader);
	kachujin->ReadMesh(L"Kachujin/Mesh");
	kachujin->ReadMaterial(L"Kachujin/Mesh");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Idle");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Walk");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Run");
	kachujin->ReadClip(L"Kachujin/Sword And Shield Slash");
	kachujin->ReadClip(L"Kachujin/Salsa Dancing");


	Transform* transform = NULL;

	transform = kachujin->AddTransform();
	transform->Position(0, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(0, 0, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(1, 1, 1.0f);

	transform = kachujin->AddTransform();
	transform->Position(-30, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(2, 2, 0.75f);

	transform = kachujin->AddTransform();
	transform->Position(15, 0, -30);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayBlendMode(3, 0, 1, 2);
	kachujin->SetBlendAlpha(3, 1.5f);

	transform = kachujin->AddTransform();
	transform->Position(30, 0, -32.5f);
	transform->Scale(0.075f, 0.075f, 0.075f);
	kachujin->PlayTweenMode(4, 4, 0.75f);

	kachujin->UpdateTransforms();

	animators.push_back(kachujin);
}

void BillboardDemo::KachujinCollider()
{
	UINT count = kachujin->GetTransformCount();
	colliders = new  ColliderObject * [count];

	colliderInitTransforms = new Transform();
	colliderInitTransforms->Position(-2.9f, 1.45f, -50.0f);
	colliderInitTransforms->Scale(5, 5, 75);

	for (UINT i = 0; i < count; i++)
	{
		colliders[i] = new ColliderObject();

		//colliders[i]->Init = new Transform();
		//colliders[i]->Init->Position(0, 0, 0);
		//colliders[i]->Init->Scale(10, 30, 10);

		colliders[i]->Transform = new Transform();
		//colliders[i]->Collider = new Collider(colliders[i]->Transform, colliders[i]->Init);
		colliders[i]->Collider = new Collider(colliders[i]->Transform, colliderInitTransforms);
	}
}

void BillboardDemo::KachujinWeapon()
{
	weapon = new ModelRender(shader);
	weapon->ReadMesh(L"Weapon/Sword");
	weapon->ReadMaterial(L"Weapon/Sword");

	UINT count = kachujin->GetTransformCount();
	for (UINT i = 0; i < count; i++)
		weapon->AddTransform();

	weapon->UpdateTransforms();
	models.push_back(weapon);


	weaponInitTransform = new Transform();
	weaponInitTransform->Position(-2.9f, 1.45f, -6.45f);
	weaponInitTransform->Scale(0.5f, 0.5f, 0.75f);
	weaponInitTransform->Rotation(0, 0, 1);
}

void BillboardDemo::PointLighting()
{
	PointLight light;
	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient
		Color(0.0f, 0.0f, 1.0f, 1.0f), //Diffuse
		Color(0.0f, 0.0f, 0.7f, 1.0f), //Specular
		Color(0.0f, 0.0f, 0.7f, 1.0f), //Emissive
		Vector3(-30, 10, -30), 5.0f, 0.9f // 위치, 범위, 강도
	};
	Lighting::Get()->AddPointLight(light);

	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f),
		Color(1.0f, 0.0f, 0.0f, 1.0f),
		Color(0.6f, 0.2f, 0.0f, 1.0f),
		Color(0.6f, 0.3f, 0.0f, 1.0f),
		Vector3(15, 10, -30), 10.0f, 0.3f
	};
	Lighting::Get()->AddPointLight(light);

	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f), //Ambient
		Color(0.0f, 1.0f, 0.0f, 1.0f), //Diffuse
		Color(0.0f, 0.7f, 0.0f, 1.0f), //Specular
		Color(0.0f, 0.7f, 0.0f, 1.0f), //Emissive
		Vector3(-5, 1, -17.5f), 5.0f, 0.9f
	};
	Lighting::Get()->AddPointLight(light);

	light =
	{
		Color(0.0f, 0.0f, 0.0f, 1.0f),
		Color(0.0f, 0.0f, 1.0f, 1.0f),
		Color(0.0f, 0.0f, 0.7f, 1.0f),
		Color(0.0f, 0.0f, 0.7f, 1.0f),
		Vector3(-10, 1, -17.5f), 5.0f, 0.9f
	};
	Lighting::Get()->AddPointLight(light);
}

void BillboardDemo::SpotLighting()
{
	SpotLight light;
	light =
	{
		Color(0.3f, 1.0f, 0.0f, 1.0f),
		Color(0.7f, 1.0f, 0.0f, 1.0f),
		Color(0.3f, 1.0f, 0.0f, 1.0f),
		Color(0.3f, 1.0f, 0.0f, 1.0f),
		Vector3(-15, 20, -30), 25.0f,
		Vector3(0, -1, 0), 30.0f, 0.4f
	};
	Lighting::Get()->AddSpotLight(light);

	light =
	{
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Color(1.0f, 0.2f, 0.9f, 1.0f),
		Vector3(0, 20, -30), 30.0f,
		Vector3(0, -1, 0), 40.0f, 0.55f
	};
	Lighting::Get()->AddSpotLight(light);
}

void BillboardDemo::Pass(UINT mesh, UINT model, UINT anim)
{
	for (MeshRender* temp : meshes)
		temp->Pass(mesh);

	for (ModelRender* temp : models)
		temp->Pass(model);

	for (ModelAnimator* temp : animators)
		temp->Pass(anim);
}

 

 

 

 

00_Light.fx 추가된 내용


더보기
...

////////////////////////////////////////////////////////////////////////////

// 다 합쳐서 리턴해주는 함수
// 매쉬, 모델, 애니메이션은 이거 쓰면 이쁘게 light를 먹는다.
float4 PS_AllLight(MeshOutput input)
{
    NormalMapping(input.Uv, input.Normal, input.Tangent);
    
    Texture(Material.Diffuse, DiffuseMap, input.Uv);
    Texture(Material.Specular, SpecularMap, input.Uv);
    
    MaterialDesc output = MakeMaterial();
    MaterialDesc result = MakeMaterial();
    
    ComputeLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    ComputePointLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    ComputeSpotLight(output, input.Normal, input.wPosition);
    AddMaterial(result, output);
    
    return float4(MaterialToColor(result).rgb, 1.0f);
}

 

 

 

 

 

 

 

96_Billboard.fx


더보기
#include "00_Global.fx"
#include "00_Light.fx"
#include "00_Render.fx"


// 모델, 매쉬 그리는 용
float4 PS(MeshOutput input) : SV_Target
{
    return PS_AllLight(input);
}

/////////////////////////////////////////////////////////////////

struct VertexBillboard
{
    float4 Position : Position;
    float2 Scale : Scale;
    uint MapIndex : MapIndex;
};

struct VertexOutput
{
    // PixelShader로 넘기는 SV_Positon이 붙었다.
    // 지오메트리로 넘어갈꺼라 SV가 붙지 않는다.(붙어도 처리는 됨, 정석상 그럼)
    float4 Position : Position;
    float2 Scale : Scale;
    uint MapIndex : MapIndex;
};


// 뒤에 기능이 추가되면 앞에 것들은 거의 하지 않음
// 그래서 VS에서는 World 변환만 함(GS에서 해도됨)
// World 변환, ViewProjection 변환은 지오메트리 셰이더에서 수행
VertexOutput VS(VertexBillboard input)
{
    VertexOutput output;
    
    output.Position = WorldPosition(input.Position);
    output.Scale = input.Scale;
    output.MapIndex = input.MapIndex;
   
    return output;
}

struct GeometryOutput
{
    // 얘가 SV_Position이 된다.(얘에서 PixelShader로 넘어갈 거니깐)
    float4 Position : SV_Position;
    float2 Uv : Uv;
    uint MapIndex : MapIndex;
};

//Maxvertexcount? 지오메트리에서 출력될 최대 개수(정점을 몇개를 내보낼 거냐)
// return은 void이다.
// 정점을 하나씩 받으면 point고, 정점을 세개씩 받으면 triangle이 붙음
// triangle 사용시에는 triangle VertexOutput input[3]가 된다.(3개의 배열이 붙음)
// 지오메트리 셰이더는 리턴 방식이 아닌 TriangleStream에 정점을 추가해주는
// 방식으로 처리
// 지오메트리는 무조건 삼각형 단위여서 TriangleStream이 붙음
// <에는 OutputType이 들어감> dx10부터는 <> template이 생겼다.
[maxvertexcount(4)]
void GS_Billboard(point VertexOutput input[1], inout TriangleStream<GeometryOutput> stream)
{
    // stream에다가 그릴려는 정점 순서대로 만들어서 넣어주면 된다.
    
    float3 up = float3(0, 1, 0);
    //float3 forward = float3(0, 0, 1);
    // 이미 월드 변환이 VS에서 해줘서 가능하다.
    // 월드를 여기서 변환해도 되지만, 정점을 펼친상태에서 하니깐 월드 변환이
    // 4번일어난다. VS에선 한번에 할 수 있다.(VS에서는 점단위로 들어오니깐)
    float3 forward = input[0].Position.xyz - ViewPosition();
    float3 right = normalize(cross(up, forward));
    
    // 크기도 만듬
    // 0번에 대해서 반을 줄여서 쓰겠다.
    float2 size = input[0].Scale * 0.5f;
    
    // 이 4개로 TriangleStrip 방식으로 작성
    float4 position[4];
 
    // size에다가 right 방향을 곱함
    // right가 1, 0, 0이라면 0.5, 0, 0이 됨
    // 그것을 input[0].Position.xyz에서 뺴줌
    // x방향으로 -0.5가됨
    // 즉, 좌하단 정점이됨(-0.5, -0.5)
    position[0] = float4(input[0].Position.xyz - size.x * right - size.y * up, 1);
    // 좌상단
    position[1] = float4(input[0].Position.xyz - size.x * right + size.y * up, 1);
    // 우하단
    position[2] = float4(input[0].Position.xyz + size.x * right - size.y * up, 1);
    // 우상단
    position[3] = float4(input[0].Position.xyz + size.x * right + size.y * up, 1);
    
    float2 uv[4] =
    {
        float2(0, 1), float2(0, 0), float2(1, 1), float2(1, 0)
    };
    
    
    GeometryOutput output;
    
    // world는 vertexShader에서 해줌
    // 정점 4개를 여기서 처리
    [unroll(4)]
    for (int i = 0; i < 4; i++)
    {
        output.Position = ViewProjection(position[i]);
        output.Uv = uv[i];
        output.MapIndex = input[0].MapIndex;
        
        // geometry에서는 triangleStrip으로 처리한다.
        // 그래서 사각형이 나옴
        // 스트림이 출력결과다.
        // Append -> 정점을 순서대로 추가 시켜주는 부분
        stream.Append(output);
    }

}

// 빌보드 효과를 없애려고 크로스 스타일로도 씀
// 정점을 8개를 내보낸다.
// 정점 하나로 면을 두개 생성하므로 maxvertexcount가 8개가 된다.
[maxvertexcount(8)]
void GS_Cross(point VertexOutput input[1], inout TriangleStream<GeometryOutput> stream)
{
    
    float3 up = float3(0, 1, 0);
    float3 forward = float3(0, 0, 1);
    float3 right = normalize(cross(up, forward));
    
    float2 size = input[0].Scale * 0.5f;
    
    float4 position[8];

    // 좌하단 정점이됨
    position[0] = float4(input[0].Position.xyz - size.x * right - size.y * up, 1);
    // 좌상단
    position[1] = float4(input[0].Position.xyz - size.x * right + size.y * up, 1);
    // 우하단
    position[2] = float4(input[0].Position.xyz + size.x * right - size.y * up, 1);
    // 우상단
    position[3] = float4(input[0].Position.xyz + size.x * right + size.y * up, 1);
    
    
    // 이번에는 forawrd 방향
     // 좌하단 정점이됨
    position[4] = float4(input[0].Position.xyz - size.x * forward - size.y * up, 1);
    // 좌상단
    position[5] = float4(input[0].Position.xyz - size.x * forward + size.y * up, 1);
    // 우하단
    position[6] = float4(input[0].Position.xyz + size.x * forward - size.y * up, 1);
    // 우상단
    position[7] = float4(input[0].Position.xyz + size.x * forward + size.y * up, 1);
    
    float2 uv[4] =
    {
        float2(0, 1), float2(0, 0), float2(1, 1), float2(1, 0)
    };
    
    GeometryOutput output;
    
    [unroll(8)]
    for (int i = 0; i < 8; i++)
    {
        output.Position = ViewProjection(position[i]);
        // 0, 1, 2, 3 순환이 되도록
        output.Uv = uv[i % 4];
        // 그대로 보내준다.
        output.MapIndex = input[0].MapIndex;
        
        stream.Append(output);
        
        // RestartStrip이란? 스트립을 다시 시작해서 추가(끊어라, 스트립을 다시 시작하라)
        // 다시 0, 1, 2, 3이 시작됨
        [flatten]
        if(i == 3)
            stream.RestartStrip();
    }

}

Texture2DArray BillboardMap;
float4 PS_Billboard(GeometryOutput input) : SV_Target
{
    // 자신의 MapIndex로 어떤 텍스쳐로 렌더링 될 것인지 정할 수 있다.
    // Texture2DArray에서 float3인데 z는 Array의 인덱스이다.
    // 밝게 하기 위해 곱해줌
    return BillboardMap.Sample(LinearSampler, float3(input.Uv, input.MapIndex)) * 1.75f;
}

technique11 T0
{
    P_VP(P0, VS_Mesh, PS)
    P_VP(P1, VS_Model, PS)
    P_VP(P2, VS_Animation, PS)

    P_BS_VGP(P3, AlphaBlend, VS, GS_Billboard, PS_Billboard)
    P_RS_BS_VGP(P4, CullMode_None, AlphaBlend_AlphaToCoverageEnable, VS, GS_Cross, PS_Billboard)
}

 

 

 

 

 

 

 

 

 

 

 

결과


 

 

'DirectX11 3D > 기본 문법' 카테고리의 다른 글

<DirectX11 3D> 100 - ColorTone(PostEffect)  (0) 2022.03.21
<DirectX11 3D> 99 - RenderTargetView  (0) 2022.03.17
<DirectX11 3D> 89 - ParticleSystem  (0) 2022.03.16
<DirectX11 3D> 88 - Snow  (0) 2022.03.14
<DirectX11 3D> 86 - AlphaBlend  (0) 2022.03.14