게임엔진/크로스플랫폼 : HazelEngine

230508 자체 엔진 개발 : Renderer2D

mrawesome 2023. 5. 26. 23:02

https://github.com/ohbumjun/GameEngineTutorial/commit/e1bf01809231e1ae4abfb9bdbf54a4169085421d

 

feat(Engine) Create basic layout for Renderer2D Class · ohbumjun/GameEngineTutorial@e1bf018

Show file tree Showing 10 changed files with 141 additions and 47 deletions.

github.com

 

void SandBox2D::OnUpdate(Hazel::Timestep ts)
{	
	HZ_PROFILE_FUNCTION();

	// Update
	{
		m_CameraController.OnUpdate(ts);
	}

	// Render
	Hazel::RenderCommand::SetClearColor({ 0.1f, 0.1f, 0.1f, 1.f });
	Hazel::RenderCommand::Clear();

	// Renderer::BeginScene(camera, lights, environment);
	// Scene 을 그리기 위해 필요한 모든 것을 한번에 그려낸다.
	{
		Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
	}

	// TODO : Shader Set Mat4, Set Float4 (Add Functions For these)
	Hazel::Renderer2D::DrawQuad({ 0.f, 0.f }, { 1.f, 1.f }, { 0.8f, 0.2f, 0.3f, 1.0f });
	Hazel::Renderer2D::DrawQuad({ 1.f, 1.f }, { 1.5f, 1.5f }, { 0.2f, 0.2f, 0.8f, 1.0f });
	Hazel::Renderer2D::DrawRotatedQuad({ -1.f, -1.f }, { 10.f, 10.f }, 
		glm::radians(5.f), m_CheckerboardTexture, 2.f, glm::vec4(1.0f, 0.9f, 0.9f, 1.0f));

	Hazel::Renderer2D::EndScene();
}

 

위의 코드 같이 특정 Layer 내에서 Rendering 을 수행할 때

간단한 API 형태로만 진행하고자 한다.

 

이를 위해서는 렌더링을 위한 세팅 및 변수 등이 외부에 노출되지 않고

특정 API 만 호출하면 원하는 형태의 rendering 이 되게 해야 한다.

 

이를 위해서 2D Rendering 전용 API 및 Class 를 만들고자 하고 이것이 바로

Renderer2D Class 이다.

 

#pragma once

#include "OrthographicCamera.h"
#include "Texture.h"

namespace Hazel
{
	// 역할 : Scene 등 데이터를 받아서, 그에 따라 GPU 측에 그려달라고 요청하는 것
	// - 해당 Class 는 super static 이 될 것이다. 즉, 아무런 data storage 도
	// - 들고 있게 하지 않을 것이라는 의미이다.
	class Renderer2D
	{
	public:
		static void Init();
		static void ShutDown();
		static void BeginScene(const OrthographicCamera& camera);
		static void EndScene();

		// Primitives
		static void DrawQuad(const glm::vec2& pos, const glm::vec2& size,
			const glm::vec4& color);
		static void DrawQuad(const glm::vec3& pos, const glm::vec2& size,
			const glm::vec4& color);
		static void DrawQuad(const glm::vec2& pos, const glm::vec2& size,
			const Ref<Texture2D>& texture, float tilingFactor = 1.f,
			const glm::vec4& tintColor = glm::vec4(1.0f));
		static void DrawQuad(const glm::vec3& pos, const glm::vec2& size,
			const Ref<Texture2D>& texture, float tilingFactor = 1.f,
			const glm::vec4& tintColor = glm::vec4(1.0f));

	};
};
#include "hzpch.h"
#include "Renderer2D.h"
#include "VertexArray.h"
#include "Shader.h"
#include "RenderCommand.h"
#include <glm/gtc/matrix_transform.hpp>

namespace Hazel
{
	struct Renderer2DData
	{
		const uint32_t MaxQuads    = 10000;
		const uint32_t MaxVertices = MaxQuads * 4;
		const uint32_t MaxIndices  = MaxQuads * 6;

		Ref<VertexArray> QuadVertexArray;
		Ref<Shader> TextureShader;
		Ref<Texture2D> WhiteTexture;
	};

	static Renderer2DData s_Data;

	void Renderer2D::Init()
	{
		HZ_PROFILE_FUNCTION();

		/*Square*/
		s_Data.QuadVertexArray = VertexArray::Create();

		// 5 floats per each vertex
		/*Vertex Pos + Texture Cordinate*/

		float squareVertices[5 * 4] = {
			-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,  /*Bottom Left  */
			0.5f, -0.5f, 0.0f, 1.0f, 0.0f,  /*Bottom Right*/
			0.5f,  0.5f, 0.0f, 1.0f, 1.0f,   /*Top Right*/
			-0.5f,  0.5f, 0.0f, 0.0f, 1.0f    /*Top Left*/
		};

		Ref<VertexBuffer> squareVB;
		squareVB = VertexBuffer::Create(squareVertices, sizeof(squareVertices));
		
		BufferLayout squareVBLayout = {
			{ShaderDataType::Float3, "a_Position"},
			{ShaderDataType::Float2, "a_TexCoord"}
		};

		squareVB->SetLayout(squareVBLayout);
		s_Data.QuadVertexArray->AddVertexBuffer(squareVB);

		uint32_t squareIndices[] = { 0, 1, 2, 2, 3, 0 };
		Ref<IndexBuffer> squareIdxB;
		squareIdxB = IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t));
		s_Data.QuadVertexArray->SetIndexBuffer(squareIdxB);

		s_Data.WhiteTexture = Texture2D::Create(1, 1);
		uint32_t whiteTextureData = 0xffffffff;
		s_Data.WhiteTexture->SetData(&whiteTextureData, /*1 * 1 */sizeof(uint32_t));

		s_Data.TextureShader = Shader::Create("assets/shaders/Texture.glsl");
		s_Data.TextureShader->Bind();
		s_Data.TextureShader->SetInt("u_Texture", 0);
	}

 

위에서 보여지듯이, Rendering 을 수행하기 위한 VertexBuffer, VertexArray, IndexBuffer 등의 내용을

Init 함수 내에서 세팅한다.

 

즉, User 입장에서는 이러한 기본적인 변수 세팅 과정을 Layer 단에서 진행해주는 것이 아니라

Render2D 라는 별도의 Class 에서 진행해주는 것이다.

 

	void Renderer2D::BeginScene(const OrthographicCamera& camera)
	{
		s_Data.TextureShader->Bind();
		s_Data.TextureShader->SetMat4(
			"u_ViewProjection", const_cast<OrthographicCamera&>(camera).GetViewProjectionMatrix());
	}

	void Renderer2D::EndScene()
	{
	}

BeginScene 은 실제 REndering 을 수행하기 전에 전체 Scene 에 적용할 사항을

세팅하는 함수이다.

 

모든 Object 들이 같은 ViewProjectionMatrix 를 사용하고 있다. 따라서 해당 함수 내에서 

ViewProjectionMatrix 를 세팅해준다.

 

 

void Renderer2D::DrawQuad(const glm::vec3& pos, const glm::vec2& size, const glm::vec4& color)
{
    // 혹시나 문제 생기면, 여기에 Shader 한번 더 bind
    s_Data.TextureShader->SetFloat4("u_Color", color);
    s_Data.TextureShader->SetFloat("m_TilingFactor", 1.0f);

    // Bind Default White Texture
    s_Data.WhiteTexture->Bind();

    // x,y 축 기준으로만 scale 을 조정할 것이다.
    glm::mat4 scale = glm::scale(glm::mat4(1.f), {size.x, size.y, 1.0f});
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) * 
        /*rotation*/ scale;

    s_Data.TextureShader->SetMat4("u_Transform", transform);

    // actual draw call
    s_Data.QuadVertexArray->Bind();
    RenderCommand::DrawIndexed(s_Data.QuadVertexArray);
}


void Renderer2D::DrawQuad(const glm::vec3& pos, const glm::vec2& size, 
    const Ref<Texture2D>& texture, float tilingFactor, const glm::vec4& tintColor)
{
    HZ_PROFILE_FUNCTION();

    s_Data.TextureShader->Bind();

    // default : 0번째 slot 에 세팅
    texture->Bind();

    // 기본 Color 로 세팅
    s_Data.TextureShader->SetFloat4("u_Color", tintColor);
    s_Data.TextureShader->SetFloat("m_TilingFactor", tilingFactor);

    // x,y 축 기준으로만 scale 을 조정할 것이다.
    glm::mat4 scale = glm::scale(glm::mat4(1.f), { size.x, size.y, 1.0f });
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) *
        /*rotation*/ scale;

    s_Data.TextureShader->SetMat4("u_Transform", transform);

    // actual draw call
    s_Data.QuadVertexArray->Bind();

    // 해당 함수안에 Texture Bind 가 존재한다.
    RenderCommand::DrawIndexed(s_Data.QuadVertexArray);
}

 

해당 함수는 실제 각 사각형을 그리고자 할 때 사용하는 함수이다.

이때 특이한 점은, 각 사각형마다 개별적으로 적용해야 할 사항들을 세팅해준다는 것이다.

 

즉, BeginScene 함수에서는 공통된 사항을 적용하는 부분이었다면

위 함수는 각각을 그릴 때 개별적인 사항을 적용하는 부분이다.