렌더링 - 소프트웨어 렌더링

Lev·2024년 3월 20일
0

📓TIL Archive

목록 보기
33/33

❗보충 필요
수학 - 내적, 외적 개념
뷰행렬 - 전치 내적 사용하는 이유, 카메라의 월드행렬의 역행렬 식
투영행렬 - 직교투영행렬과 원근투영행렬 차이
(블로그 글 백업해두기)

💁‍♀️ .oO(WinAPI 환경에서 3D 렌더링을 구현해보자!)

렌더링이란?
: 모니터에 그림을 그리는 것

모니터에 그림을 그릴 픽셀을 선별하기 위해서는, 도형이 필요하다.
따라서 점(vertex)이 중요하다!
이때 점의 위치(XYZW)를 Fvector로 표현한다.
아래의 예시에서는 삼각형을 그려주는 Polygon()을 사용하고, 3개의 버텍스를 입력해준다.

행렬 없이 직접 변환

먼저 카메라와 모니터를 고려하지 않고, 단순히 윈도우 창에 메쉬를 그리는 것부터 구현해보자!

2차원에 그리기

FTransform Transform;
float Speed = 100.0f;
float RotSpeed = 360.0f;
FVector DirNormal;
FVector DirRNormal;

void USoftRenderingLevel::Render(float _DeltaTime)
{
	ULevel::Render(_DeltaTime);

	// 윈도우 창 백버퍼이미지의 핸들 가지고 오기
	std::shared_ptr<UWindowImage> Image = GEngine->MainWindow.GetBackBufferImage();
	HDC DC = Image->GetImageDC();

	// 키 입력에 따른 버텍스 위치 변환
	if (UEngineInput::IsPress('Q'))	// 회전(반시계)
	{
		Transform.AddRotation(FVector(0.0f, 0.0f, -1.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress('E'))	// 회전(시계)
	{
		Transform.AddRotation(FVector(0.0f, 0.0f, 1.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress('W'))	// 이동(앞)
	{
		Transform.AddPosition(DirNormal * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('S'))	// 이동(뒤)
	{
		Transform.AddPosition(-DirNormal * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('A'))	// 이동(왼쪽)
	{
		Transform.AddPosition(-DirRNormal * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('D'))	// 이동(오른쪽)
	{
		Transform.AddPosition(DirRNormal * Speed * _DeltaTime);
	}

	// 원점을 기준으로 그린 도형 => FBX 파일, 메쉬 파일
	std::vector<std::vector<FVector>> Mesh =
	{
		{ {0.0, -50.0f}, {-50.0f, 50.0f}, {50.0f, 50.0f} },
		{ { 0.0, -45.0f }, { 0.0, -55.0f }, { 0.0, -45.0f } },
	};

	// 변환된 위치의 메쉬
	std::vector<std::vector<FVector>> RenderingMesh = Mesh;

	// <변환1. 회전>
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			RenderingMesh[TriCount][VertexCount].RotationZToDeg(Transform.GetRotation().Z);
		}
	}

	// 회전한 위치를 기준으로 상하좌우 방향 결정
	DirNormal = RenderingMesh[0][0].Normalize2DReturn();
	DirRNormal= float4::VectorRotationZToDeg(DirNormal, 90.0f);
	/*
	사실 위 경우는 이등변삼각형이라 이 방법으로 한번 구현해봤을 뿐,
	다른 형태의 메쉬이고 3차원일 경우에는 다른 방법을 사용해야 한다.
	*/

	// <변환2. 이동>
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			RenderingMesh[TriCount][VertexCount] += Transform.GetPosition();
		}
	}
	/*
	반드시 회전 이후에 이동이 수행되어야 한다.
	이동이 먼저 이뤄질 경우, 이동된 위치에서 원점을 기준으로 공전이 이루어지기 때문이다.
	*/

	// 변환된 버텍스를 픽셀로 전환
	std::vector < std::vector<POINT>> WinApiPoints;
	WinApiPoints.resize(RenderingMesh.size());
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		WinApiPoints[TriCount].resize(RenderingMesh[TriCount].size());
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			WinApiPoints[TriCount][VertexCount] = RenderingMesh[TriCount][VertexCount].ConvertToWinApiPOINT();
		}
	}

	// 화면에 메쉬 그리기
	for (size_t TriCount = 0; TriCount < WinApiPoints.size(); TriCount++)
	{
		POINT& StartPoint = WinApiPoints[TriCount][0];
		Polygon(DC, &StartPoint, 3);
	}
}

3차원에 그리기 (1)

변환 순서를 반드시 지켜야 한다!
크기 -> 자전 -> 이동 -> 공전 -> 부모

FTransform Transform;
float Speed = 100.0f;
float RotSpeed = 360.0f;

void USoftRenderingLevel::BeginPlay()
{
	ULevel::BeginPlay();

	Transform.SetScale({ 100, 100, 100 });	// 크기 정보
}

void USoftRenderingLevel::Render(float _DeltaTime)
{
	ULevel::Render(_DeltaTime);

	std::shared_ptr<UWindowImage> Image = GEngine->MainWindow.GetBackBufferImage();
	HDC DC = Image->GetImageDC();

	// 원점을 기준으로 그리는 메쉬
	std::vector<std::vector<FVector>> Mesh;
	
	Mesh.resize(4);
	// 도형의 뒷면
	Mesh[0] = { {-0.5f, -0.5f, -0.5f }, {-0.5f, 0.5f, -0.5f}, {0.5f, 0.5f, -0.5f} };
	Mesh[1] = { {-0.5f, -0.5f, -0.5f}, {0.5f, -0.5f, -0.5f}, {0.5f, 0.5f, -0.5f} };
	// 도형의 앞면
	Mesh[2] = { FVector::VectorRotationYToDeg(Mesh[0][0], 180.0f), FVector::VectorRotationYToDeg(Mesh[0][1], 180.0f), FVector::VectorRotationYToDeg(Mesh[0][2], 180.0f) };
	Mesh[3] = { FVector::VectorRotationYToDeg(Mesh[1][0], 180.0f), FVector::VectorRotationYToDeg(Mesh[1][1], 180.0f), FVector::VectorRotationYToDeg(Mesh[1][2], 180.0f) };

	// 키입력에 따른 변환값
	if (UEngineInput::IsPress(VK_NUMPAD8))	// 회전(Z축)
	{
		Transform.AddRotation(FVector(0.0f, 0.0f, 1.0f) * RotSpeed* _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD7))	// 회전(Z축)
	{
		Transform.AddRotation(FVector(0.0f, 0.0f, -1.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD5))	// 회전(Y축)
	{
		Transform.AddRotation(FVector(0.0f, 1.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD4))	// 회전(Y축)
	{
		Transform.AddRotation(FVector(0.0f, -1.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD2))	// 회전(X축)
	{
		Transform.AddRotation(FVector(1.0f, 0.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD1))	// 회전(X축)
	{
		Transform.AddRotation(FVector(-1.0f, 0.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress('W'))	// 이동(위)
	{
		Transform.AddPosition(FVector::Up * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('S'))	// 이동(아래)
	{
		Transform.AddPosition(FVector::Down * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('A'))	// 이동(왼쪽)
	{
		Transform.AddPosition(FVector::Left * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('D'))	// 이동(오른쪽)
	{
		Transform.AddPosition(FVector::Right * Speed * _DeltaTime);
	}
	
    // 변환될 메쉬
	std::vector<std::vector<FVector>> RenderingMesh = Mesh;

	FMatrix Scale;
	Scale.Scale(Transform.GetScale());
	
	// <변환1. 크기>
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			RenderingMesh[TriCount][VertexCount] *= Scale;
		}
	}

	// <변환2. 회전>
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			RenderingMesh[TriCount][VertexCount].RotationXToDeg(Transform.GetRotation().X);
			RenderingMesh[TriCount][VertexCount].RotationYToDeg(Transform.GetRotation().Y);
			RenderingMesh[TriCount][VertexCount].RotationZToDeg(Transform.GetRotation().Z);
		}
	}

	// <변환3. 이동>
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			RenderingMesh[TriCount][VertexCount] += Transform.GetPosition();
		}
	}
	
	// 변환된 버텍스를 화면상의 픽셀로 전환
	std::vector < std::vector<POINT>> WinApiPoints;
	WinApiPoints.resize(RenderingMesh.size());
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		WinApiPoints[TriCount].resize(RenderingMesh[TriCount].size());
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			WinApiPoints[TriCount][VertexCount] = RenderingMesh[TriCount][VertexCount].ConvertToWinApiPOINT();
		}
	}

	// 화면에 메쉬 그리기
	for (size_t TriCount = 0; TriCount < WinApiPoints.size(); TriCount++)
	{
		POINT& StartPoint = WinApiPoints[TriCount][0];
		Polygon(DC, &StartPoint, 3);
	}
}

3차원 변환행렬 적용

로컬 스페이스 → 월드 스페이스 → 뷰 스페이스 → 투영 스페이스 → 모니터

메쉬 * 월드행렬 * 뷰행렬 * 투영행렬 * 뷰포트 => 모니터에 출력!

- 로컬 스페이스 : 물체의 메쉬만 존재하는 상태
- 월드 스페이스 : 각각의 물체의 메쉬마다 월드정보를 적용하여 모두 같은 월드에 존재하는 상태
- 뷰 스페이스 : 플레이어의 뒤에 놓인 카메라가 세상의 원점에 위치하고 정면을 바라보도록 모든 물체를 정렬한 상태
- 투영 스페이스 : 카메라의 시야 범위 속 물체들을 3D 공간에서 2D 화면으로 투영한 상태
- 모니터 : 해상도에 따른 비율에 맞춰 메쉬가 그려짐

크기 → 자전 → 이동 → 공전 → 부모
위 순서로 메쉬를 변환해주다보면, 모든 버텍스에 같은 과정을 수행해주게 된다.
따라서 모든 벡터를 일일이 변환시키지 않고, 행렬을 이용하면 편하다.
연산하는 메소드를 호출하는 횟수도 줄기 때문에 상대적으로 효율적이다.
이때 행렬은 차원이나 마찬가지이다.
공간을 정의하는 크기, 회전, 위치 등의 정보를 포함하고 있기 때문이다.
4 x 4 행렬에 월드의 공간 정보를 기록하여 버텍스의 위치 정보에 곱하자!

월드 행렬

크기, 회전, 이동에 관한 월드 정보를 가진 행렬이다.

/*
[크기 행렬]
크기X		0			0			0
0			크기Y		0			0
0			0			크기Z		0
0			0			0			1
*/

void Scale(float4 _Value)
{
	Identity();
	Arr2D[0][0] = _Value.X;
	Arr2D[1][1] = _Value.Y;
	Arr2D[2][2] = _Value.Z;
	Arr2D[3][3] = 1.0f;
}

/*
--------------------------------------------------------------------------------
[회전 행렬]
			X좌표값		Y좌표값		Z좌표값
X축 =>		회전			회전			회전			0
Y축 =>		회전			회전			회전			0
Z축 =>		회전			회전			회전			0
			0			0			0			1

1. X축 회전
1		0		0		0
0		cos		sin		0
0		-sin	cos		0
0		0		0		1

2. Y축 회전
cos		0		-sin	0
0		1		0		0
sin		0		cos		0
0		0		0		1

3. Z축 회전
cos		sin		0		0
-sin	cos		0		0
0		0		1		0
0		0		0		1

- 코시시코를 기억하자!
- X축 회전이라면, X축을 기준으로는 아무 영향도 받지 않고, X좌표값도 변하지 않는다.
- 회전 정보는 크기 정보에 해당되는 범위도 포함하고 있다.
  회전 변환 시 눈에 보이는 크기도 함께 변환되기 때문이다.
  따라서 반드시 크기 -> 회전 변환 순서를 지켜줘야 한다.
- 사실 이 변환식은 윈도우 좌표계에 해당하는 것이고, 다른 좌표계를 사용하면 식이 조금 달라진다.
*/

float4x4 RotationDeg(const float4 _Value)
{
	Identity();
	float4x4 X;
	X.RotationXDeg(_Value.X);
	float4x4 Y;
	Y.RotationYDeg(_Value.Y);
	float4x4 Z;
	Z.RotationZDeg(_Value.Z);
	return X * Y * Z;
}

void RotationXDeg(float _Angle)
{
	RotationXRad(_Angle * UEngineMath::DToR);
}

void RotationXRad(float _Angle)
{
	Identity();
	Arr2D[1][1] = cosf(_Angle);
	Arr2D[1][2] = sinf(_Angle);
	Arr2D[2][1] = -sinf(_Angle);
	Arr2D[2][2] = cosf(_Angle);
}

void RotationYDeg(float _Angle)
{
	RotationYRad(_Angle * UEngineMath::DToR);
}

void RotationYRad(float _Angle)
{
	Identity();
	Arr2D[0][0] = cosf(_Angle);
	Arr2D[0][2] = -sinf(_Angle);
	Arr2D[2][0] = sinf(_Angle);
	Arr2D[2][2] = cosf(_Angle);
}

void RotationZDeg(float _Angle)
{
	RotationZRad(_Angle * UEngineMath::DToR);
}

void RotationZRad(float _Angle)
{
	Identity();
	Arr2D[0][0] = cosf(_Angle);
	Arr2D[0][1] = sinf(_Angle);
	Arr2D[1][0] = -sinf(_Angle);
	Arr2D[1][1] = cosf(_Angle);
}

/*
--------------------------------------------------------------------------------
[이동 행렬]
1			0			0			0
0			1			0			0
0			0			1			0
이동X		이동Y		이동Z		1

- 버텍스의 W값이 0이 아니어야만 이동 값을 더해줄 수 있다.
- 보통은 1을 넣지만, W값을 바꾸어 위치 적용 %를 변경해줄 수 있다.
*/

void Position(float4 _Value)
{
	Identity();
	Arr2D[3][0] = _Value.X;
	Arr2D[3][1] = _Value.Y;
	Arr2D[3][2] = _Value.Z;
}

뷰 행렬

월드에는 카메라가 실제로 존재하지 않기 때문에,
카메라를 제외한 모든 물체가 함께 움직여 마치 카메라가 움직이는 것처럼 보이게 한다.
이를 위해 카메라를 원점에 위치시키고 정면(Z축 양의 방향)을 바라보도록 월드를 정렬하는 과정은,
카메라를 제외한 나머지 모든 물체들을 카메라의 로컬 스페이스로 데려가는 것이라고 생각해도 무방하다.
따라서 모든 물체에 카메라의 월드행렬의 역행렬을 적용시켜야 한다.

/*
<회전행렬을 구하는 방법>
(1) 3축을 기준으로 회전한 각도를 이용하여 계산하는 방법
(2) 회전행렬의 두 벡터를 알고 있을 경우, 세 벡터가 서로 직교한다는 점을 이용하여 외적으로 계산하는 방법
-> 아래에선 2번 방법을 사용한다.
*/

float4x4 View(const float4 _EyePos, const float4 _EyeDir, const float4 _EyeUp)
{
	/*
	_EyePos	=> 위치
	_EyeDir	=> 바라보는 방향
	_EyeUp	=> 바라보는 방향과 수직인 벡터
	
	여기서 회전행렬의 두 벡터를 입력받기 때문에, 외적을 통해 나머지 한 벡터를 알아내어
	카메라의 회전벡터를 구할 수 있다.
	*/
    
    float4x4 View;
    
    FVector Up = _EyeUp.Normalize3DReturn();
	FVector Forward = _EyeDir.Normalize3DReturn();
	FVector Right = FVector::Cross3D(Up, Forward);	// 외적
	Up.W = 0.0f;
	Forward.W = 0.0f;
	Right.W = 0.0f;
    
    // 회전 정보
	View.ArrVector[0] = Right;
	View.ArrVector[1] = Up;
	View.ArrVector[2] = Forward;
	View.Transpose();

	float4 NegEyePos = -_EyePos;
	NegEyePos.W = 1.0f;
    
    // 이동 정보
	View.ArrVector[3].X = float4::DotProduct3D(Right, NegEyePos);
	View.ArrVector[3].Y = float4::DotProduct3D(Up, NegEyePos);
	View.ArrVector[3].Z = float4::DotProduct3D(Forward, NegEyePos);

	*this = View;

	return View;
}

투영 행렬

// (1) 직교투영행렬
void OrthographicLH(float _Width, float _Height, float _Near, float _Far)
{
	// _Near : 가장 가까이 보이는 평면
	// _Far : 가장 멀리 보이는 평면

	Identity();
    
	float fRange = 1.0f / (_Far - _Near);
    
    // 크기 정보
	Arr2D[0][0] = 2.0f / _Width;
	Arr2D[1][1] = 2.0f / _Height;
	Arr2D[2][2] = fRange;
    
    // 이동 정보
	Arr2D[3][2] = -fRange * _Near;	// Near 앞쪽은 출력될 필요가 없기 때문

}

// (2) 원근투영행렬
/* TBC */

뷰포트

void ViewPort(float _Width, float _Height, float _Left, float _Right, float _ZMin, float _ZMax)
{
	Identity();
    
    // 크기 정보 
	Arr2D[0][0] = _Width * 0.5f;
	Arr2D[1][1] = -_Height * 0.5f;
	Arr2D[2][2] = _ZMax != 0.0f ? 1.0f : _ZMin / _ZMax;
    
    // 이동 정보
	Arr2D[3][0] = Arr2D[0][0] + _Left;
	Arr2D[3][1] = -Arr2D[1][1] + _Right;
	Arr2D[3][2] = _ZMax != 0.0f ? 0.0f : _ZMin / _ZMax;
	Arr2D[3][3] = 1.0f;
}

3차원에 그리기 (2)

FTransform PlayerTransform;	// 플레이어에게 적용되는 월드행렬
FTransform CameraTransform;	// 카메라에게 적용되는 월드행렬

float Speed = 500.0f;
float RotSpeed = 360.0f;

void USoftRenderingLevel::BeginPlay()
{
	ULevel::BeginPlay();

	PlayerTransform.SetScale({100, 100, 100});
	CameraTransform.SetPosition({ 0, 0, -1000 });
}

void USoftRenderingLevel::Tick(float _DeltaTime)
{
	ULevel::Tick(_DeltaTime);

	PlayerControl(_DeltaTime);
	CameraControl(_DeltaTime);
}

void USoftRenderingLevel::PlayerControl(float _DeltaTime)
{
	// 키입력에 따른 플레이어 회전 변화
	if (UEngineInput::IsPress(VK_NUMPAD8))	// 회전(Z축)
	{
		PlayerTransform.AddRotation(FVector(0.0f, 0.0f, 1.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD7))	// 회전(Z축)
	{
		PlayerTransform.AddRotation(FVector(0.0f, 0.0f, -1.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD5))	// 회전(Y축)
	{
		PlayerTransform.AddRotation(FVector(0.0f, 1.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD4))	// 회전(Y축)
	{
		PlayerTransform.AddRotation(FVector(0.0f, -1.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD2))	// 회전(X축)
	{
		PlayerTransform.AddRotation(FVector(1.0f, 0.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_NUMPAD1))	// 회전(X축)
	{
		PlayerTransform.AddRotation(FVector(-1.0f, 0.0f, 0.0f) * RotSpeed * _DeltaTime);
	}

	FMatrix RotationMat, RotationXMat, RotationYMat, RotationZMat;
	RotationXMat.RotationXDeg(PlayerTransform.GetRotation().X);
	RotationYMat.RotationYDeg(PlayerTransform.GetRotation().Y);
	RotationZMat.RotationZDeg(PlayerTransform.GetRotation().Z);
	RotationMat = RotationXMat * RotationYMat * RotationZMat;
	
	// 키입력에 따른 플레이어 이동 변화
	if (UEngineInput::IsPress('W'))	// 이동(위)
	{
		PlayerTransform.AddPosition(RotationMat.UpVector() * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('S'))	// 이동(아래)
	{
		PlayerTransform.AddPosition(RotationMat.DownVector() * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('A'))	// 이동(왼쪽)
	{
		PlayerTransform.AddPosition(RotationMat.LeftVector() * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress('D'))	// 이동(오른쪽)
	{
		PlayerTransform.AddPosition(RotationMat.RightVector() * Speed * _DeltaTime);
	}
}

void USoftRenderingLevel::CameraControl(float _DeltaTime)
{
	FMatrix RotationMat, RotationXMat, RotationYMat, RotationZMat;
	RotationXMat.RotationXDeg(CameraTransform.GetRotation().X);
	RotationYMat.RotationYDeg(CameraTransform.GetRotation().Y);
	RotationZMat.RotationZDeg(CameraTransform.GetRotation().Z);
	RotationMat = RotationXMat * RotationYMat * RotationZMat;
	
	// 키입력에 따른 카메라 이동 변화
	if (UEngineInput::IsPress(VK_LEFT))	// 이동(왼쪽)
	{
		CameraTransform.AddPosition(-RotationMat.RightVector() * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_RIGHT))	// 이동(오른쪽)
	{
		CameraTransform.AddPosition(RotationMat.RightVector() * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_UP))	// 이동(위)
	{
		CameraTransform.AddPosition(RotationMat.UpVector() * Speed * _DeltaTime);
	}

	if (UEngineInput::IsPress(VK_DOWN))	// 이동(아래)
	{
		CameraTransform.AddPosition(RotationMat.UpVector() * Speed * _DeltaTime);
	}
    
    if (UEngineInput::IsPress('O'))	// 회전(Y축)
	{
		CameraTransform.AddRotation({0.0f, 360.0f * _DeltaTime, 0.0f});
	}

	if (UEngineInput::IsPress('P'))	// 회전(Y축)
	{
		CameraTransform.AddRotation({ 0.0f, 360.0f * _DeltaTime, 0.0f });
	}
}

void USoftRenderingLevel::Render(float _DeltaTime)
{
	ULevel::Render(_DeltaTime);

	std::shared_ptr<UWindowImage> Image = GEngine->MainWindow.GetBackBufferImage();
	HDC DC = Image->GetImageDC();
	
	// 로컬 스페이스
	std::vector<std::vector<FVector>> Mesh;

	Mesh.resize(4);
	Mesh[0] = { {-0.5f, -0.5f, -0.5f }, {-0.5f, 0.5f, -0.5f}, {0.5f, 0.5f, -0.5f} };
	Mesh[1] = { {-0.5f, -0.5f, -0.5f}, {0.5f, -0.5f, -0.5f}, {0.5f, 0.5f, -0.5f} };
	Mesh[2] = { FVector::VectorRotationYToDeg(Mesh[0][0], 180.0f), FVector::VectorRotationYToDeg(Mesh[0][1], 180.0f), FVector::VectorRotationYToDeg(Mesh[0][2], 180.0f) };
	Mesh[3] = { FVector::VectorRotationYToDeg(Mesh[1][0], 180.0f), FVector::VectorRotationYToDeg(Mesh[1][1], 180.0f), FVector::VectorRotationYToDeg(Mesh[1][2], 180.0f) };

	std::vector<std::vector<FVector>> RenderingMesh = Mesh;
	
	// [월드 행렬]
	FMatrix World;
	{
		// 플레이어 크기 정보
		FMatrix ScaleMat;
		ScaleMat.Scale(PlayerTransform.GetScale());
		// 플레이어 회전(자전) 정보
		FMatrix RotationMat, RotationXMat, RotationYMat, RotationZMat;
		RotationXMat.RotationXDeg(PlayerTransform.GetRotation().X);
		RotationYMat.RotationYDeg(PlayerTransform.GetRotation().Y);
		RotationZMat.RotationZDeg(PlayerTransform.GetRotation().Z);
		RotationMat = RotationXMat * RotationYMat * RotationZMat;
		// 플레이어 이동 정보
		FMatrix PositionMat;
		PositionMat.Position(PlayerTransform.GetPosition());
		World = ScaleMat * RotationMat * PositionMat;	// 크기 * 자전 * 이동
	}
	
	// [뷰 행렬]
	FMatrix View;
	{
		// 카메라 회전(자전) 정보
		FMatrix CameraRotationMat, CameraRotationXMat, CameraRotationYMat, CameraRotationZMat;
		CameraRotationXMat.RotationXDeg(CameraTransform.GetRotation().X);
		CameraRotationYMat.RotationYDeg(CameraTransform.GetRotation().Y);
		CameraRotationZMat.RotationZDeg(CameraTransform.GetRotation().Z);
		CameraRotationMat = CameraRotationXMat * CameraRotationYMat * CameraRotationZMat;

		View.View(CameraTransform.GetPosition(), CameraRotationMat.ForwardVector(), CameraRotationMat.UpVector());
	}
    
    // [투영 행렬]
    FMatrix Projection;
	{
		Projection.OrthographicLH(1280.0f, 640.0f, 1.0f, 1000.0f);
	}
    
    // [뷰포트]
	FMatrix ViewPort;
	{
		ViewPort.ViewPort(1280.0f, 640.0f, 0.0f, 0.0f, 0.0f, 1.0f);
	}

	// 변환!
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			// <변환1. (로컬 스페이스 * 월드 행렬) -> 월드 스페이스>
            RenderingMesh[TriCount][VertexCount] *= World;
            // <변환2. (월드 스페이스 * 뷰 행렬) -> 뷰 스페이스>
            RenderingMesh[TriCount][VertexCount] *= View;
            // <변환3. (뷰 스페이스 * 투영 행렬) -> 투영 스페이스>
            RenderingMesh[TriCount][VertexCount] *= Projection;
		}
	}
    
    for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			// <변환4. (투영 스페이스 * 뷰포트) -> 변환 완료>
			RenderingMesh[TriCount][VertexCount] *= ViewPort;
		}
	}
	
	// 변환 완료된 메쉬의 버텍스를 픽셀로 전환
	std::vector < std::vector<POINT>> WinApiPoints;
	WinApiPoints.resize(RenderingMesh.size());
	for (size_t TriCount = 0; TriCount < RenderingMesh.size(); TriCount++)
	{
		WinApiPoints[TriCount].resize(RenderingMesh[TriCount].size());
		for (size_t VertexCount = 0; VertexCount < RenderingMesh[TriCount].size(); VertexCount++)
		{
			WinApiPoints[TriCount][VertexCount] = RenderingMesh[TriCount][VertexCount].ConvertToWinApiPOINT();
		}
	}
	
	// 화면에 메쉬 그리기
	for (size_t TriCount = 0; TriCount < WinApiPoints.size(); TriCount++)
	{
		POINT& StartPoint = WinApiPoints[TriCount][0];
		Polygon(DC, &StartPoint, 3);
	}
}
profile
⋆꙳⊹⋰ 𓇼⋆ 𝑻𝑰𝑳 𝑨𝑹𝑪𝑯𝑰𝑽𝑬 ⸝·⸝⋆꙳⊹⋰

0개의 댓글