1. 텍스처

오늘은 사각형뿐만 아니라 큐브,구,그리드를 텍스처있는 형태로 만들어 볼 것이다.

이때 uv 좌표를 사용하게 될 것인데 uv 좌표는 전에 말했던 것 처럼 결국엔 텍스처의 비율이라는 것이다. 좌표에 uv 시작점 그리고 비율을 넘겨주면 텍스처가 따라서 맵핑되는 것이다.

 

먼저 uv좌표를 받고 텍스처가 샘플링된 uv좌표로 들어갈 수 있도록 fx 코드를 만들어주자

World.fx

matrix World;
matrix View;
matrix Projection;
Texture2D Texture0;

struct VertexInput
{
	float4 position : POSITION;
    float2 uv : TEXCOORD;
};

struct VertexOutput
{
	float4 position : SV_POSITION;
    float2 uv : TEXCOORD;
};

VertexOutput VS(VertexInput input)
{
	VertexOutput output;
    output.position = mul(input.position,World);
    output.position = mul(output.position, View);
    output.position = mul(output.position, Projection);
	

    output.uv = input.uv;

	return output;
}

SamplerState Sampler0;

float4 PS(VertexOutput input) : SV_TARGET
{
	//텍스처 -> 샘플링된 좌표 
    return Texture0.Sample(Sampler0, input.uv);
}

//와이어프레임모드
RasterizerState FillModeWireFrame
{
    FillMode = Wireframe;
};

technique11 T0
{
	//하나의 통로
	pass P0
	{
		SetVertexShader(CompileShader(vs_5_0, VS()));
		SetPixelShader(CompileShader(ps_5_0, PS()));
	}

	pass P1
	{
        SetRasterizerState(FillModeWireFrame); 
		SetVertexShader(CompileShader(vs_5_0, VS()));
		SetPixelShader(CompileShader(ps_5_0, PS()));
	}
};

 

그리고 GeometryHelper에서 사각형이지만 Color를 받는 버전이 아닌 VertexTextureData를 사용하는 버전을 새로 만들어주자

GeometryHelper.cpp

void GeometryHelper::CreateQuad(shared_ptr<Geometry<VertexTextureData>> geometry)
{
	vector<VertexTextureData> vtx;

	vtx.resize(4);


	vtx[0].position = Vec3(-0.5f, -0.5f, 0.f);
	vtx[0].uv = Vec2(0.f, 1.f);
	vtx[1].position = Vec3(-0.5f, 0.5f, 0.f);
	vtx[1].uv = Vec2(0.f, 0.f);
	vtx[2].position = Vec3(0.5f, -0.5f, 0.f);
	vtx[2].uv = Vec2(1.f, 1.f);
	vtx[3].position = Vec3(0.5f, 0.5f, 0.f);
	vtx[3].uv = Vec2(1.f, 0.f);
	geometry->SetVertices(vtx);

	//1 3	보통 시계방향
	//0 2
	vector<uint32> idx = { 0,1,2,2,1,3 };
	geometry->SetIndices(idx);
}

 

그리고 새로운 Demo를 만들어 줄 것인데 이전과 코드가 같지만 Texture 변수를 저장할 스마트 포인터변수와 리소스매니저를 통해 텍스처를 경로상에서 로드하고이를 shader 클래스의 GetSRV를 통해 ShaderResourceView로 우리가 지정해준 shader fx파일의 Texture0과 연결시켜주면 된다. 

TextureDemo.h

#pragma once

#include "IExecute.h"
#include "Geometry.h"

class TextureDemo : public IExecute
{
public:
	void Init() override;
	void Update() override;
	void Render() override;


	shared_ptr<Shader> _shader;
	
	// Object
	shared_ptr<Geometry<VertexTextureData>> _geometry;
	shared_ptr<VertexBuffer> _vertexBuffer;
	shared_ptr<IndexBuffer> _indexBuffer;
	Matrix _world=Matrix::Identity;

	//Camera
	shared_ptr<GameObject> _camera;

	shared_ptr<Texture> _texture;
};

 

TextureDemo.cpp

#include "pch.h"
#include "05. TextureDemo.h"
#include "GeometryHelper.h"
#include "Camera.h"
#include "CameraScript.h"

void TextureDemo::Init()
{
	_shader = make_shared<Shader>(L"04. World.fx");

	//Object
	_geometry = make_shared<Geometry<VertexTextureData>>();
	GeometryHelper::CreateQuad(_geometry);
	//고속복사
	_vertexBuffer = make_shared<VertexBuffer>();
	_vertexBuffer->Create(_geometry->GetVertices());
	_indexBuffer = make_shared<IndexBuffer>();
	_indexBuffer->Create(_geometry->GetIndices());

	// Camera
	_camera = make_shared<GameObject>();
	_camera->GetOrAddTransform();
	_camera->AddComponent(make_shared<Camera>());
	_camera->AddComponent(make_shared<CameraScript>());
	_camera->GetTransform()->SetPosition(Vec3(0.f, 0.f, -2.f));

	//Texture
	_texture = RESOURCES->Load<Texture>(L"Veigar", L"..\\Resources\\Textures\\veigar.jpg");
}

void TextureDemo::Update()
{
	_camera->Update();
}

void TextureDemo::Render()
{
	_shader->GetMatrix("World")->SetMatrix((float*)&_world);
	_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
	_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());

	uint32 stride = _vertexBuffer->GetStride();
	uint32 offset = _vertexBuffer->GetOffset();

	DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
	
	//_buffer->GetCount()
	_shader->DrawIndexed(0, 0, _indexBuffer->GetCount(), 0, 0);
}

 

이렇게 해주면 베이가가 나타난다

 

2.Geometry

이제 큐브, 구, 그리드 도형도 만들어 줄 것이다.

1. 큐브

큐브를 만들때는 정점만으로는 최소 8개가 필요하지만 uv매핑을 하려면 각면 마다 4개씩 24개가 있어야 한다. 

그에 따른 인덱스도 지정해주어야한다. 인덱스는 한면마다 6개씩 총 36개가 있어야한다.

GeometryHelper.cpp

void GeometryHelper::CreateCube(shared_ptr<Geometry<VertexTextureData>> geometry)
{
	//x y z
	float w2 = 0.5f;
	float h2 = 0.5f;
	float d2 = 0.5f;

	vector<VertexTextureData> vtx(24);

	// 앞면
	vtx[0] = VertexTextureData{ Vec3(-w2, -h2, -d2), Vec2(0.0f, 1.0f) };
	vtx[1] = VertexTextureData{ Vec3(-w2, +h2, -d2), Vec2(0.0f, 0.0f) };
	vtx[2] = VertexTextureData{ Vec3(+w2, +h2, -d2), Vec2(1.0f, 0.0f) };
	vtx[3] = VertexTextureData{ Vec3(+w2, -h2, -d2), Vec2(1.0f, 1.0f) };
	// 뒷면
	vtx[4] = VertexTextureData{ Vec3(-w2, -h2, +d2), Vec2(1.0f, 1.0f) };
	vtx[5] = VertexTextureData{ Vec3(+w2, -h2, +d2), Vec2(0.0f, 1.0f) };
	vtx[6] = VertexTextureData{ Vec3(+w2, +h2, +d2), Vec2(0.0f, 0.0f) };
	vtx[7] = VertexTextureData{ Vec3(-w2, +h2, +d2), Vec2(1.0f, 0.0f) };
	// 윗면
	vtx[8] = VertexTextureData{ Vec3(-w2, +h2, -d2), Vec2(0.0f, 1.0f) };
	vtx[9] = VertexTextureData{ Vec3(-w2, +h2, +d2), Vec2(0.0f, 0.0f) };
	vtx[10] = VertexTextureData{ Vec3(+w2, +h2, +d2), Vec2(1.0f, 0.0f) };
	vtx[11] = VertexTextureData{ Vec3(+w2, +h2, -d2), Vec2(1.0f, 1.0f) };
	// 아랫면
	vtx[12] = VertexTextureData{ Vec3(-w2, -h2, -d2), Vec2(1.0f, 1.0f) };
	vtx[13] = VertexTextureData{ Vec3(+w2, -h2, -d2), Vec2(0.0f, 1.0f) };
	vtx[14] = VertexTextureData{ Vec3(+w2, -h2, +d2), Vec2(0.0f, 0.0f) };
	vtx[15] = VertexTextureData{ Vec3(-w2, -h2, +d2), Vec2(1.0f, 0.0f) };
	// 왼쪽면
	vtx[16] = VertexTextureData{ Vec3(-w2, -h2, +d2), Vec2(0.0f, 1.0f) };
	vtx[17] = VertexTextureData{ Vec3(-w2, +h2, +d2), Vec2(0.0f, 0.0f) };
	vtx[18] = VertexTextureData{ Vec3(-w2, +h2, -d2), Vec2(1.0f, 0.0f) };
	vtx[19] = VertexTextureData{ Vec3(-w2, -h2, -d2), Vec2(1.0f, 1.0f) };
	// 오른쪽면
	vtx[20] = VertexTextureData{ Vec3(+w2, -h2, -d2), Vec2(0.0f, 1.0f) };
	vtx[21] = VertexTextureData{ Vec3(+w2, +h2, -d2), Vec2(0.0f, 0.0f) };
	vtx[22] = VertexTextureData{ Vec3(+w2, +h2, +d2), Vec2(1.0f, 0.0f) };
	vtx[23] = VertexTextureData{ Vec3(+w2, -h2, +d2), Vec2(1.0f, 1.0f) };

	geometry->SetVertices(vtx);

	vector<uint32> idx(36);

	// 앞면
	idx[0] = 0; idx[1] = 1; idx[2] = 2;
	idx[3] = 0; idx[4] = 2; idx[5] = 3;
	// 뒷면
	idx[6] = 4; idx[7] = 5; idx[8] = 6;
	idx[9] = 4; idx[10] = 6; idx[11] = 7;
	// 윗면
	idx[12] = 8; idx[13] = 9; idx[14] = 10;
	idx[15] = 8; idx[16] = 10; idx[17] = 11;
	// 아랫면
	idx[18] = 12; idx[19] = 13; idx[20] = 14;
	idx[21] = 12; idx[22] = 14; idx[23] = 15;
	// 왼쪽면
	idx[24] = 16; idx[25] = 17; idx[26] = 18;
	idx[27] = 16; idx[28] = 18; idx[29] = 19;
	// 오른쪽면
	idx[30] = 20; idx[31] = 21; idx[32] = 22;
	idx[33] = 20; idx[34] = 22; idx[35] = 23;

	geometry->SetIndices(idx);
}

 

이렇게 해주고 TextureDemo에서 GeometryHelper 내장함수를 부르는 것을 CreateCube로 바꿔주면 텍스처가 적용된 

큐브가 나오게 된다.

 

 

2.구

구를 만들 때는 전체 구를 만든 다음 위 아래 끝점에서 도넛모양으로 여러개로 자르고 그 도넛을 여러개로 잘라서 만들고 위 아래를 삼각형으로 연속적으로 이어주면 된다.

설계 그림

GeometryHelper.cpp

void GeometryHelper::CreateSphere(shared_ptr<Geometry<VertexTextureData>> geometry)
{
	//옵션, 인자로 받아도되는 것
	float radius = 0.5f; // 구의 반지름
	uint32 stackCount = 20; // 가로 분할
	uint32 sliceCount = 20; // 세로 분할

	vector<VertexTextureData> vtx;

	VertexTextureData v;

	// 북극
	//제일 위가 0,0
	v.position = Vec3(0.0f, radius, 0.0f);
	v.uv = Vec2(0.5f, 0.0f);
	vtx.push_back(v);

	//가로 분할
	float stackAngle = XM_PI / stackCount;
	//도넛 분할
	float sliceAngle = XM_2PI / sliceCount;

	float deltaU = 1.f / static_cast<float>(sliceCount);
	float deltaV = 1.f / static_cast<float>(stackCount);

	// 고리마다 돌면서 정점을 계산한다 (북극/남극 단일점은 고리가 X)
	for (uint32 y = 1; y <= stackCount - 1; ++y)
	{
		float phi = y * stackAngle;

		// 고리에 위치한 정점
		for (uint32 x = 0; x <= sliceCount; ++x)
		{
			float theta = x * sliceAngle;

			v.position.x = radius * sinf(phi) * cosf(theta);
			v.position.y = radius * cosf(phi);
			v.position.z = radius * sinf(phi) * sinf(theta);

			v.uv = Vec2(deltaU * x, deltaV * y);

			vtx.push_back(v);
		}
	}

	// 남극
	v.position = Vec3(0.0f, -radius, 0.0f);
	v.uv = Vec2(0.5f, 1.0f);
	vtx.push_back(v);

	geometry->SetVertices(vtx);

	vector<uint32> idx(36);

	// 북극 인덱스
	for (uint32 i = 0; i <= sliceCount; ++i)
	{
		//  [0]
		//   |  \
		//  [i+1]-[i+2]
		idx.push_back(0);
		idx.push_back(i + 2);
		idx.push_back(i + 1);
	}

	// 몸통 인덱스
	//도넛 - 도넛 사이 삼각형
	uint32 ringVertexCount = sliceCount + 1;
	for (uint32 y = 0; y < stackCount - 2; ++y)
	{
		for (uint32 x = 0; x < sliceCount; ++x)
		{
			//  [y, x]-[y, x+1]
			//  |		/
			//  [y+1, x]
			idx.push_back(1 + (y)*ringVertexCount + (x));
			idx.push_back(1 + (y)*ringVertexCount + (x + 1));
			idx.push_back(1 + (y + 1) * ringVertexCount + (x));
			//		 [y, x+1]
			//		 /	  |
			//  [y+1, x]-[y+1, x+1]
			idx.push_back(1 + (y + 1) * ringVertexCount + (x));
			idx.push_back(1 + (y)*ringVertexCount + (x + 1));
			idx.push_back(1 + (y + 1) * ringVertexCount + (x + 1));
		}
	}

	// 남극 인덱스
	uint32 bottomIndex = static_cast<uint32>(vtx.size()) - 1;
	uint32 lastRingStartIndex = bottomIndex - ringVertexCount;
	for (uint32 i = 0; i < sliceCount; ++i)
	{
		//  [last+i]-[last+i+1]
		//  |      /
		//  [bottom]
		idx.push_back(bottomIndex);
		idx.push_back(lastRingStartIndex + i);
		idx.push_back(lastRingStartIndex + i + 1);
	}

	geometry->SetIndices(idx);
}

 

이렇게 해주고 호출 부분을 CreateSphere로 바꿔주면 구가 생성된다. 2번째 그림은 와이어프레임 모드가 적용된 버전

으로 아까 말한 원리가 더 잘 이해되게 생성되어있다.

 

3.그리드

 

그리드는 격자모양의 판으로 나중에 지형을 생성할 때 사용된다. 이 그리드를 만들려면 각 선 사이를 정점으로 나눠주면 되는데 이때 정점의 수는 지정해줄 격의 수보다 하나 많으면 된다. 그럼 이 그리드를 만들기 위해서는 x y의 사이즈를 지정해주어야한다. 

GeometryHelper.cpp

void GeometryHelper::CreateGrid(shared_ptr<Geometry<VertexTextureData>> geometry, int32 sizeX, int32 sizeY)
{
	vector<VertexTextureData> vtx;

	for (int32 z = 0; z < sizeZ + 1; z++)
	{
		for (int32 x = 0; x < sizeX + 1; x++)
		{
			VertexTextureData v;
			//각 정점
			v.position = Vec3(static_cast<float>(x), 0, static_cast<float>(z));
			v.uv = Vec2(static_cast<float>(x), static_cast<float>(z));

			vtx.push_back(v);
		}
	}

	geometry->SetVertices(vtx);

	vector<uint32> idx;

	for (int32 z = 0; z < sizeZ; z++)
	{
		for (int32 x = 0; x < sizeX; x++)
		{
			//  [0]
			//   |	\
			//  [2] - [1]
			idx.push_back((sizeX + 1) * (z + 1) + (x));
			idx.push_back((sizeX + 1) * (z)+(x + 1));
			idx.push_back((sizeX + 1) * (z)+(x));
			//  [1] - [2]
			//   	\  |
			//		  [0]
			idx.push_back((sizeX + 1) * (z)+(x + 1));
			idx.push_back((sizeX + 1) * (z + 1) + (x));
			idx.push_back((sizeX + 1) * (z + 1) + (x + 1));
		}
	}

	geometry->SetIndices(idx);
}

 

TextureDemo에서 Geometry->CreateGrid를 해주게 되면 그리드가 생성된다. 이때 텍스처가 맵핑 제대로 되지 않는 이유는 우리가 uv좌표가 1이 넘어가는 경우 어떻게 해줄지를 아직 구현하지 않았기 때문이다.

 

 

3.Sampling

이번에는 텍스처를 어떻게 샘플링해줄 것인지 정해줄 것이다. 이때 사용되는 것은 필터와 Address인데 

필터는 확대 / 축소 시 중간값을 어떻게 처리해주는지에 관한 정보이고 

Address는 uv값이 1보다 컸을 때, 나머지 부분을 어떻게 처리해주는지에 관한 정보이다.

 

일단 fx 파일에서 Wrap, Mirror, Clamp, Border 4가지 모드를 만들어주고 테스트 해보자 이때 모드의 적용을 Texture의 샘플

을 설정하는 PS 함수 단계에서 실행하게 해주자

Sampler.fx

matrix World;
matrix View;
matrix Projection;
Texture2D Texture0;
uint Address;


struct VertexInput
{
	float4 position : POSITION;
    float2 uv : TEXCOORD;
};

struct VertexOutput
{
	float4 position : SV_POSITION;
    float2 uv : TEXCOORD;
};

VertexOutput VS(VertexInput input)
{
	VertexOutput output;
    output.position = mul(input.position,World);
    output.position = mul(output.position, View);
    output.position = mul(output.position, Projection);
	

    output.uv = input.uv;

	return output;
}

//Filter - 확대/ 축소 시 중간값 처리방식
// Address - uv가 1보다 컸을 때, 나머지 부분 처리방식
SamplerState Sampler0;


SamplerState SamplerAddressWrap
{
    AddressU = Wrap;
    AddressV = Wrap;
};

SamplerState SamplerAddressMirror
{
    AddressU = Mirror;
    AddressV = Mirror;
};

SamplerState SamplerAddressClamp
{
    AddressU = Clamp;
    AddressV = Clamp;
};

SamplerState SamplerAddressBorder
{
    AddressU = Border;
    AddressV = Border;
    BorderColor = float4(1, 0, 0, 1);
};

float4 PS(VertexOutput input) : SV_TARGET
{
	if(Address==0)
        return Texture0.Sample(SamplerAddressWrap, input.uv);
    
    if (Address == 1)
        return Texture0.Sample(SamplerAddressMirror, input.uv);
    
    if (Address == 2)
        return Texture0.Sample(SamplerAddressClamp, input.uv);
    
    if (Address == 3)
        return Texture0.Sample(SamplerAddressBorder, input.uv);
	//텍스처 -> 샘플링된 좌표 
    return Texture0.Sample(Sampler0, input.uv);
}

technique11 T0
{
	//하나의 통로
	pass P0
	{
		SetVertexShader(CompileShader(vs_5_0, VS()));
		SetPixelShader(CompileShader(ps_5_0, PS()));
	}
};

 

이렇게 해준다음 Address의 값을 구조체를 선언하고 이 값에 따라 바뀔 수 있도록 해주자

SamplerDemo.cpp

void SamplerDemo::Render()
{
	_shader->GetMatrix("World")->SetMatrix((float*)&_world);
	_shader->GetMatrix("View")->SetMatrix((float*)&Camera::S_MatView);
	_shader->GetMatrix("Projection")->SetMatrix((float*)&Camera::S_MatProjection);
	_shader->GetSRV("Texture0")->SetResource(_texture->GetComPtr().Get());

	enum ADDRESS_VALUE
	{
		ADDRESS_WRAP=0,
		ADDRESS_MIRROR=1,
		ADDRESS_CLAMP=2,
		ADDRESS_BORDER=3,
	};

	_shader->GetScalar("Address")->SetInt(ADDRESS_WRAP);

	uint32 stride = _vertexBuffer->GetStride();
	uint32 offset = _vertexBuffer->GetOffset();

	DC->IASetVertexBuffers(0, 1, _vertexBuffer->GetComPtr().GetAddressOf(), &stride, &offset);
	DC->IASetIndexBuffer(_indexBuffer->GetComPtr().Get(), DXGI_FORMAT_R32_UINT, 0);
	
	//_buffer->GetCount()
	_shader->DrawIndexed(0, 0, _indexBuffer->GetCount(), 0, 0);
}

 

1. Wrap - 모든 텍스처가 동일

 

2.Mirror - 각 이미지들이 거울을 보는 것처럼 대칭 형태

 

3.Clamp - 기본

4.Border - 원래 텍스처를 제외하고는 지정한 색상으로 유지

 

+ Recent posts