Link

์ง€๋‚œ ์ฑ•ํ„ฐ์—์„œ ์„ค๋ช…ํ•œ RenderObjects๋ฅผ ์‚ฌ์šฉํ•ด ์ƒˆ๋กœ์šด ๋ Œ๋”๋ง ๋ฃจํ”„๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๊ฒƒ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด์ „์—๋Š” GLTF๋กœ๋ถ€ํ„ฐ ๋ถˆ๋Ÿฌ์˜จ ๋ฉ”์‹œ ๋ชฉ๋ก์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ Œ๋”๋ง์„ ํ•˜๋“œ์ฝ”๋”ฉํ–ˆ์ง€๋งŒ, ์ด์ œ๋Š” ํ•ด๋‹น ๋ฆฌ์ŠคํŠธ๋ฅผ RenderObjects๋กœ ๋ณ€ํ™˜ํ•œ ๋’ค ์ด๋ฅผ ํ†ตํ•ด ๋ Œ๋”๋งํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. GLTF๋กœ๋ถ€ํ„ฐ ์•„์ง์€ ํ…์Šค์ณ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ธฐ๋Šฅ์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ธฐ๋ณธ ๋จธํ…Œ๋ฆฌ์–ผ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์•„ํ‚คํ…์ฒ˜ ๊ตฌ์ถ•์€ vk_types.h์— ๊ธฐ๋ณธ ์”ฌ ๋…ธ๋“œ ๊ตฌ์กฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์‹œ์ž‘ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

struct DrawContext;

// base class for a renderable dynamic object
class IRenderable {

    virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx) = 0;
};

// implementation of a drawable scene node.
// the scene node can hold children and will also keep a transform to propagate
// to them
struct Node : public IRenderable {

    // parent pointer must be a weak pointer to avoid circular dependencies
    std::weak_ptr<Node> parent;
    std::vector<std::shared_ptr<Node>> children;

    glm::mat4 localTransform;
    glm::mat4 worldTransform;

    void refreshTransform(const glm::mat4& parentMatrix)
    {
        worldTransform = parentMatrix * localTransform;
        for (auto c : children) {
            c->refreshTransform(worldTransform);
        }
    }

    virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx)
    {
        // draw children
        for (auto& c : children) {
            c->Draw(topMatrix, ctx);
        }
    }
};

Node๋Š” ์šฐ๋ฆฌ๊ฐ€ ๊ตฌํ˜„ํ•˜๋Š” ์ฒซ IRenderable์ด ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋…ธ๋“œ ํŠธ๋ฆฌ๋ฅผ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ตฌ์„ฑํ•˜๋ฉฐ, ๋ถ€๋ชจ ํฌ์ธํ„ฐ๋Š” ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด weak_ptr๋กœ ์ €์žฅํ•˜๊ณ , ์ž์‹ ๋…ธ๋“œ๋“ค์€ shared_ptr๋กœ ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Node ํด๋ž˜์Šค๋Š” ๋ณ€ํ™˜์— ํ•„์š”ํ•œ ํ–‰๋ ฌ ์ •๋ณด๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค. ๋กœ์ปฌ ๋ณ€ํ™˜๊ณผ ์›”๋“œ ๋ณ€ํ™˜ ๋ชจ๋‘ ํฌํ•จ๋˜๋ฉฐ, ๋กœ์ปฌ ๋ณ€ํ™˜์ด ๋ณ€๊ฒฝ๋  ๊ฒฝ์šฐ ์›”๋“œ ๋ณ€ํ™˜์„ ๊ฐฑ์‹ ํ•ด์•ผ ํ•˜๋ฏ€๋กœ refreshTransform() ํ•จ์ˆ˜๋ฅผ ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋…ธ๋“œ ํŠธ๋ฆฌ๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ์ˆœํšŒํ•˜๋ฉฐ ํ–‰๋ ฌ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ฐฑ์‹ ๋˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

draw ํ•จ์ˆ˜๋Š” ์ง์ ‘์ ์ธ ๋ Œ๋”๋ง์€ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ณ  ์ž์‹ ๋…ธ๋“œ๋“ค์˜ Draw()๋งŒ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธฐ๋ณธ Node ํด๋ž˜์Šค๋Š” ์‹ค์ œ ๋ Œ๋”๋ง์„ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, ๋ฉ”์‹œ๋ฅผ ์ถœ๋ ฅํ•  ์ˆ˜ ์žˆ๋Š” MeshNode ํด๋ž˜์Šค๋ฅผ vk_engine.h์— ์ถ”๊ฐ€ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

struct MeshNode : public Node {

	std::shared_ptr<MeshAsset> mesh;

	virtual void Draw(const glm::mat4& topMatrix, DrawContext& ctx) override;
};

MeshNode๋Š” ๋ฉ”์‹œ ์—์…‹์— ๋Œ€ํ•œ ํฌ์ธํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, draw ํ•จ์ˆ˜๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œํ•ด drawContext์— ๋“œ๋กœ์šฐ ๋ช…๋ น์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ DrawContext๋„ ์ž‘์„ฑํ•ฉ์‹œ๋‹ค. ๊ตฌํ˜„์€ vk_engine.h์—์„œ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

struct RenderObject {
	uint32_t indexCount;
	uint32_t firstIndex;
	VkBuffer indexBuffer;

	MaterialInstance* material;

	glm::mat4 transform;
	VkDeviceAddress vertexBufferAddress;
};

struct DrawContext {
	std::vector<RenderObject> OpaqueSurfaces;
};

ํ˜„์žฌ DrawContext๋Š” ๋‹จ์ˆœํžˆ RenderObject ๊ตฌ์กฐ์ฒด์˜ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. RenderObject๊ฐ€ ๋ Œ๋”๋ง์˜ ํ•ต์‹ฌ ์š”์†Œ์ž…๋‹ˆ๋‹ค. ์—”์ง„ ์ž์ฒด๋Š” Node ํด๋ž˜์Šค์—์„œ ์–ด๋– ํ•œ Vulkan ํ•จ์ˆ˜๋„ ์ง์ ‘ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  ๋ Œ๋”๋Ÿฌ๊ฐ€ DrawContext๋กœ๋ถ€ํ„ฐ RenderObjects์˜ ๋ฐฐ์—ด์„ ๋ฐ›์•„ ๋งค ํ”„๋ ˆ์ž„์„ ๊ตฌ์„ฑ(ํ˜น์€ ์บ์‹ฑ)ํ•œ ํ›„, ๊ฐ ๊ฐ์ฒด์— ๋Œ€ํ•ด ํ•˜๋‚˜์˜ Vulkan ๋“œ๋กœ์šฐ์ฝœ์„ ์‹คํ–‰ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ์ •์˜๋˜์—ˆ์œผ๋ฏ€๋กœ, MeshNode์˜ Draw() ํ•จ์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

void MeshNode::Draw(const glm::mat4& topMatrix, DrawContext& ctx)
{
	glm::mat4 nodeMatrix = topMatrix * worldTransform;

	for (auto& s : mesh->surfaces) {
		RenderObject def;
		def.indexCount = s.count;
		def.firstIndex = s.startIndex;
		def.indexBuffer = mesh->meshBuffers.indexBuffer.buffer;
		def.material = &s.material->data;

		def.transform = nodeMatrix;
		def.vertexBufferAddress = mesh->meshBuffers.vertexBufferAddress;
		
		ctx.OpaqueSurfaces.push_back(def);
	}

	// recurse down
	Node::Draw(topMatrix, ctx);
}

๋ฉ”์‹œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋จธํ…Œ๋ฆฌ์–ผ์€ ๊ฐ–๋Š” ์—ฌ๋Ÿฌ ํ‘œ๋ฉด์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๋ฉ”์‹œ์˜ ํ‘œ๋ฉด์„ ์ˆœํšŒํ•˜๋ฉด์„œ ๊ฐ๊ฐ์— ๋Œ€ํ•ด RenderObject๋ฅผ ์ƒ์„ฑํ•ด ๋ชฉ๋ก์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ํ–‰๋ ฌ์„ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•˜๋Š” ์ง€์— ์ฃผ๋ชฉํ•˜์„ธ์š”. ๊ฐ์ฒด๋ฅผ ๋…ธ๋“œ์˜ WorldTransform๋งŒ์œผ๋กœ ์‚ฝ์ž…ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, TopMatrix์™€ ๊ณฑํ•œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” Draw() ํ•จ์ˆ˜๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ ํ˜ธ์ถœ๋˜๋”๋ผ๋„ ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์„œ๋กœ ๋‹ค๋ฅธ ๋ณ€ํ™˜์œผ๋กœ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋œป์ด๋ฉฐ, ๋™์ผํ•œ ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ์œ„์น˜์— ๋ Œ๋”๋งํ•ด์•ผ ํ•  ๋•Œ ๋งค์šฐ ์œ ์šฉํ•œ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ํ•ด์•ผ ํ•  ์ผ์€ VulkanEngine ํด๋ž˜์Šค์— ๊ฐ์ฒด๋ฅผ ๊ทธ๋ฆฌ๋Š” ๋ฃจํ”„๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ DrawContext๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ์‹ค์ œ Vulkan ํ˜ธ์ถœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด ๊ธฐ์กด์— ํ•˜๋“œ์ฝ”๋”ฉ๋˜์–ด ์žˆ๋˜ ์‚ฌ๊ฐํ˜• ๋ฉ”์‹œ์™€ ์›์ˆญ์ด ๋จธ๋ฆฌ๋ฅผ ๊ทธ๋ฆฌ๋˜ ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค. draw_geometry() ํ•จ์ˆ˜์—์„œ ์ฒซ ๋ฒˆ์งธ ์‚ผ๊ฐํ˜•์„ ๊ทธ๋ฆฌ๋Š” ์ดํ›„์˜ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ๋ชฉ๋ก์„ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด DrawContext ๊ตฌ์กฐ์ฒด๋ฅผ VulkanEngine ํด๋ž˜์Šค์— ์ถ”๊ฐ€ํ•˜๊ณ , Vulkan ๋ Œ๋”๋ง ๋ฃจํ”„ ์™ธ๋ถ€์—์„œ ๋…ธ๋“œ๋“ค์˜ Draw() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  update_scene() ํ•จ์ˆ˜๋„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋˜ํ•œ ๋ถˆ๋Ÿฌ์˜จ ๋ฉ”์‹œ๋“ค์„ ๋‹ด๊ธฐ ์œ„ํ•œ Node ๊ฐ์ฒด์˜ ํ•ด์‹œ ๋งต๋„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์นด๋ฉ”๋ผ ์„ค์ • ๊ฐ™์€ ์”ฌ ๊ด€๋ จ ๋กœ์ง๋„ ํ•จ๊ป˜ ์ฒ˜๋ฆฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

class VulkanEngine{
    DrawContext mainDrawContext;
    std::unordered_map<std::string, std::shared_ptr<Node>> loadedNodes;

    void update_scene();
}

์ด ์ฝ”๋“œ๋ฅผ draw_geometry์— GPUSceneData ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹์„ ์ƒ์„ฑํ•œ ์งํ›„์— ์ถ”๊ฐ€ํ•˜์—ฌ ๋ฐ”์ธ๋”ฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด์— ์›์ˆญ์ด ๋จธ๋ฆฌ๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉ์œผ๋กœ ๊ทธ๋ ธ๋˜ ์ฝ”๋“œ๋ฅผ ์ด ์ฝ”๋“œ๋กœ ๋Œ€์ฒดํ•˜์„ธ์š”. ๋‹จ, ์”ฌ ๋ฐ์ดํ„ฐ ๋””์Šคํฌ๋ฆฝํ„ฐ ์…‹ ํ• ๋‹น ๋ถ€๋ถ„์€ ์ด ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉ๋˜๋ฏ€๋กœ ๊ทธ๋Œ€๋กœ ๋‚จ๊ฒจ๋‘ก๋‹ˆ๋‹ค.

	for (const RenderObject& draw : mainDrawContext.OpaqueSurfaces) {

		vkCmdBindPipeline(cmd,VK_PIPELINE_BIND_POINT_GRAPHICS, draw.material->pipeline->pipeline);
		vkCmdBindDescriptorSets(cmd,VK_PIPELINE_BIND_POINT_GRAPHICS,draw.material->pipeline->layout, 0,1, &globalDescriptor,0,nullptr );
		vkCmdBindDescriptorSets(cmd,VK_PIPELINE_BIND_POINT_GRAPHICS,draw.material->pipeline->layout, 1,1, &draw.material->materialSet,0,nullptr );

		vkCmdBindIndexBuffer(cmd, draw.indexBuffer,0,VK_INDEX_TYPE_UINT32);

		GPUDrawPushConstants pushConstants;
		pushConstants.vertexBuffer = draw.vertexBufferAddress;
		pushConstants.worldMatrix = draw.transform;
		vkCmdPushConstants(cmd,draw.material->pipeline->layout ,VK_SHADER_STAGE_VERTEX_BIT,0, sizeof(GPUDrawPushConstants), &pushConstants);

		vkCmdDrawIndexed(cmd,draw.indexCount,1,draw.firstIndex,0,0);
	}

RenderObject๋Š” ์„ค๊ณ„ ๋‹น์‹œ Vulkan์˜ ๋‹จ์ผ ๊ทธ๋ฆฌ๊ธฐ ๋ช…๋ น์œผ๋กœ ์ง์ ‘ ๋ณ€ํ™˜๋˜๋„๋ก ์˜๋„๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์ž์›์„ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  vkCmdDraw๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ๋ณ„๋‹ค๋ฅธ ๋กœ์ง์ด ์—†์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” ๋งค ๊ทธ๋ฆฌ๊ธฐ ๋งˆ๋‹ค ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜๊ณ  ์žˆ์–ด ๋น„ํšจ์œจ์ ์ด์ง€๋งŒ, ์ด๋Š” ์ถ”ํ›„์— ๊ฐœ์„ ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ ์ง€๋‚œ ์ฑ•ํ„ฐ์—์„œ ๋ถˆ๋Ÿฌ์˜จ ๋ฉ”์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด ๋ช‡ ๊ฐœ์˜ Node๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ์ด๋ฅผ ๊ทธ๋ ค ๋ฉ”์‹œ๋ฅผ DrawContext์— ์ถ”๊ฐ€ํ•˜๋Š” ์ž‘์—…์ด ๋‚จ์•„์žˆ์Šต๋‹ˆ๋‹ค. loadGLTFMeshes ํ•จ์ˆ˜๋Š” ๋จธํ…Œ๋ฆฌ์–ผ์„ ์ œ๋Œ€๋กœ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์ง€๋งŒ, ๊ธฐ๋ณธ ๋จธํ…Œ๋ฆฌ์–ผ์„ ์ ์šฉํ•ด์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์šฐ์„  vk_loader.h์˜ GeoSurface ๊ตฌ์กฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•˜์—ฌ ๋จธํ…Œ๋ฆฌ์–ผ ์ •๋ณด๋ฅผ ๋‹ด๋„๋ก ํ•ฉ์‹œ๋‹ค.

struct GLTFMaterial {
	MaterialInstance data;
};

struct GeoSurface {
	uint32_t startIndex;
	uint32_t count;
	std::shared_ptr<GLTFMaterial> material;
};

๋‹ค์Œ์€ vk_engine.cpp์˜ init_default_dataํ•จ์ˆ˜์—์„œ ๊ธฐ๋ณธ ๋จธํ…Œ๋ฆฌ์–ผ์„ ์ƒ์„ฑํ•œ ์ดํ›„์˜ ๋งˆ์ง€๋ง‰ ๋ถ€๋ถ„์„ ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

	for (auto& m : testMeshes) {
		std::shared_ptr<MeshNode> newNode = std::make_shared<MeshNode>();
		newNode->mesh = m;

		newNode->localTransform = glm::mat4{ 1.f };
		newNode->worldTransform = glm::mat4{ 1.f };

		for (auto& s : newNode->mesh->surfaces) {
			s.material = std::make_shared<GLTFMaterial>(defaultData);
		}

		loadedNodes[m->name] = std::move(newNode);
	}

๊ฐ ํ…Œ์ŠคํŠธ ๋ฉ”์‹œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด MeshNode๋ฅผ ์ƒ์„ฑํ•˜๊ณ , ํ•ด๋‹น ๋ฉ”์‹œ ์—์…‹์„ ํ•ด๋‹น ๋…ธ๋“œ์˜ shared_ptr๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ๋จธํ…Œ๋ฆฌ์–ผ๋„ ๋น„์Šทํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์šฐ๋ฆฌ๊ฐ€ ๊ฐ์ฒด๋ฅผ ์ด๋Ÿฌํ•œ ๋ฐฉ์‹์œผ๋กœ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, GLTF๋กœ๋ถ€ํ„ฐ ๋…ธ๋“œ, ๋ฉ”์‹œ, ๋จธํ…Œ๋ฆฌ์–ผ์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ง์ ‘ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ GLTF ๋ฐ์ดํ„ฐ์—์„œ๋Š” ์—ฌ๋Ÿฌ ๋…ธ๋“œ๊ฐ€ ํ•˜๋‚˜์˜ ๋ฉ”์‹œ๋ฅผ ์ฐธ์กฐํ•˜๊ฑฐ๋‚˜, ์—ฌ๋Ÿฌ ๋ฉ”์‹œ๊ฐ€ ๋™์ผํ•œ ๋จธํ…Œ๋ฆฌ์–ผ์„ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋น„๋ก ์ง€๊ธˆ์€ ๋ถˆํ•„์š”ํ•ด ๋ณด์ผ ์ˆ˜ ์žˆ์ง€๋งŒ ์ด๋Ÿฐ ๊ฒฝ์šฐ์—๋Š” shared_ptr์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด์ œ update_scene() ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋ด…์‹œ๋‹ค. ์ง€๋‚œ ์ฑ•ํ„ฐ์—์„œ ์›์ˆญ์ด ๋จธ๋ฆฌ์— ์ ์šฉํ–ˆ๋˜ ์นด๋ฉ”๋ผ ๋กœ์ง๋„ ์ด ํ•จ์ˆ˜๋กœ ์˜ฎ๊ฒจ์˜ฌ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

void VulkanEngine::update_scene()
{
	mainDrawContext.OpaqueSurfaces.clear();

	loadedNodes["Suzanne"]->Draw(glm::mat4{1.f}, mainDrawContext);	

	sceneData.view = glm::translate(glm::vec3{ 0,0,-5 });
	// camera projection
	sceneData.proj = glm::perspective(glm::radians(70.f), (float)_windowExtent.width / (float)_windowExtent.height, 10000.f, 0.1f);

	// invert the Y direction on projection matrix so that we are more similar
	// to opengl and gltf axis
	sceneData.proj[1][1] *= -1;
	sceneData.viewproj = sceneData.proj * sceneData.view;

	//some default lighting parameters
	sceneData.ambientColor = glm::vec4(.1f);
	sceneData.sunlightColor = glm::vec4(1.f);
	sceneData.sunlightDirection = glm::vec4(0,1,0.5,1.f);
}

๋จผ์ € DrawContext์—์„œ RenderObject๋“ค์„ ์ดˆ๊ธฐํ™”ํ•œ ํ›„, loadedNode๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ ๋ฉ”์‹œ ์ด๋ฆ„์ด Suzanne์ธ ์›์ˆญ์ด ๋ฉ”์‹œ์— ๋Œ€ํ•ด Draw๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” darw() ํ•จ์ˆ˜์˜ ๊ฐ€์žฅ ์ฒ˜์Œ, ํ”„๋ ˆ์ž„ ํŽœ์Šค๋ฅผ ๋Œ€๊ธฐํ•˜๊ธฐ ์ „์— ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.

void VulkanEngine::draw()
{
	update_scene();

	//wait until the gpu has finished rendering the last frame. Timeout of 1 second
	VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000));
}

์ด์ œ ์—”์ง„์„ ์‹คํ–‰ํ•ด๋ณด๋ฉด, ์›์ˆญ์ด ๋จธ๋ฆฌ๊ฐ€ ์œ„์—์„œ ๋น„์ถ”๋Š” ๋“œ๋ผ๋งˆํ‹ฑํ•œ ์กฐ๋ช… ์•„๋ž˜์— ๋ Œ๋”๋ง ๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ์›์ˆญ์ด ๋จธ๋ฆฌ๊ฐ€ ํฐ์ƒ‰์ด ์•„๋‹ˆ๋ผ ์—ฌ๋Ÿฌ ์ƒ‰์œผ๋กœ ๋ณด์ธ๋‹ค๋ฉด, vk_loader.cpp์˜ OverrideColors๊ฐ€ false๋กœ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์„ธ์š”.

์ด์ œ ์ด๋ฅผ ์‹œ์—ฐํ•˜๊ธฐ ์œ„ํ•ด Node๋ฅผ ์กฐ์ž‘ํ•˜๊ณ  ๋ Œ๋”๋ง ๋™์ž‘์„ ์กฐ๊ธˆ ๋ณ€๊ฒฝํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ €, ํ๋ธŒ๋ฅผ ๊ทธ๋ฆฌ๋Š” Node๋ฅผ ๊ฐ€์ ธ์™€ ์—ฌ๋Ÿฌ ๋ฒˆ ๊ทธ๋ ค ํ๋ธŒ๋กœ ์ด๋ฃจ์–ด์ง„ ์„ ์„ ๋งŒ๋“ค ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๋…ธ๋“œ๋“ค์€ ํ•ด์‹œ ๋งต์— ์ €์žฅ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์›ํ•˜๋Š” ๋ฐฉ์‹๋Œ€๋กœ ๊ฐœ๋ณ„์ ์œผ๋กœ ์ ‘๊ทผํ•˜๊ณ  ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์ž‘์—…์€ update_scene ํ•จ์ˆ˜์— ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

	for (int x = -3; x < 3; x++) {

		glm::mat4 scale = glm::scale(glm::vec3{0.2});
		glm::mat4 translation =  glm::translate(glm::vec3{x, 1, 0});

		loadedNodes["Cube"]->Draw(translation * scale, mainDrawContext);
	}

ํ๋ธŒ๋ฅผ ํฌ๊ธฐ๋ฅผ ์ž‘๊ฒŒ ์ค„์ธ ๋’ค, ํ™”๋ฉด์˜ ์™ผ์ชฝ์—์„œ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ์ด๋™์‹œํ‚ค๋Š” ๋ณ€ํ™˜์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ Draw๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. Draw๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๋งˆ๋‹ค ์„œ๋กœ ๋‹ค๋ฅธ ํ–‰๋ ฌ์„ ๊ฐ€์ง„ RenderObject๊ฐ€ DrawContext์— ์ถ”๊ฐ€๋˜๋ฏ€๋กœ, ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ์œ„์น˜์— ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์œผ๋กœ 4์žฅ์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์žฅ์—์„œ๋Š” GLTF ๋กœ๋”๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜์—ฌ ํ…์Šค์ณ์™€ ์—ฌ๋Ÿฌ ๊ฐ์ฒด๊ฐ€ํฌํ•จ๋œ ์”ฌ์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ์ƒํ˜ธ์ž‘์šฉ ๊ฐ€๋Šฅํ•œ FPS ์นด๋ฉ”๋ผ๋ฅผ ์„ค์ •ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Next: Chapter 5: Interactive Camera