栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Cherno

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Cherno

文章目录
  • 74. Where to go next + Code Review Game Engine series
  • 75. Entity Component System Game Engine series
  • 76. Intro to EnTT (ECS) Game Engine series
  • 77. Entities and Components Game Engine series
  • 78. The ENTITY Class Game Engine series
  • 79. Camera Systems Game Engine series
  • 80. Scene Camera Game Engine series
  • 81. Native Scripting Game Engine series
  • 82. Native Scripting (now with virtual functions!)
  • 83. Scene Hierarchy Panel Game Engine series
  • 84. Properties Panel Game Engine series
  • 85. Camera Component UI
  • 86. Drawing Component UI
  • 87. Transform Component UI
  • 88. Adding Removing Entities and Components UI
  • 89. Making the Hazelnut Editor Look Good!
  • 90. Saving and Loading Scenes!
  • 91. Open Save File Dialogs
  • 92. Transformation Gizmos
  • 93. Editor Camera
  • 94. Multiple Render Targets and Framebuffer Refactor
  • 95. Preparing Framebuffers for Mouse Picking
  • 96. Clearing Framebuffer Texture Attachments + Git Branching
  • 97. Mouse Picking
  • 98. Clicking to Select Entities
  • 99. SPIR-V and the New Shader System
  • 注:

74. Where to go next + Code Review Game Engine series 75. Entity Component System Game Engine series 76. Intro to EnTT (ECS) Game Engine series

这里cherno简单介绍了一下 entt 的用法,比如:

class MeshComponent;

static void OnTransformConstruct(entt::registry& registry, entt::entity entity)
{

}

struct TransformComponent
{
    glm::mat4 Transform;

    TransformComponent() = default;
    TransformComponent(const TransformComponent&) = default;
    TransformComponent(const glm::mat4& transform)
        : Transform(transform) {}

    operator glm::mat4& () { return Transform; }
    operator const glm::mat4& () const { return Transform; }
};

entt::entity entity = m_Registry.create();
m_Registry.emplace(entity, glm::mat4(1.0f));

m_Registry.on_construct().connect<&OnTransformConstruct>();

TransformComponent& transform = m_Registry.get(entity);

auto view = m_Registry.view();
for (auto entity : view)
{
    TransformComponent& transform = view.get(entity);
}

auto group = m_Registry.group(entt::get);
for (auto entity : group)
{
    auto&[transform, mesh] = group.get(entity);

    Renderer::Submit(mesh, transform);
}

而我们所需要的只不过是:#include "entt.hpp"

77. Entities and Components Game Engine series 78. The ENTITY Class Game Engine series

我们在写一个 ENTITY 类的时候,本质上在这里是对 entt 库的一个包装,包装成我们自己想要的api形式。但是我们的成员中按理不能有一个 Scene,比如不能是 Ref,因为一个 entity 并不拥有场景,但是又需要比如场景是否存在来做判定。因此按理来说比较好的做法是弱引用之类的。不过我们这里就简单地写成一个裸指针了。

79. Camera Systems Game Engine series 80. Scene Camera Game Engine series 81. Native Scripting Game Engine series

我们希望可以做成类似这样的:

这里我们叫做 NativeScriptComponent ,因为之后想让 ScriptComponent 作为 C# 或是 lua 之类的。

82. Native Scripting (now with virtual functions!) 83. Scene Hierarchy Panel Game Engine series

这里提了一下:imgui中有一个 imgui_demo.cpp 文件,给了imgui的所有(?)用法。貌似是和 showdemowindow 对应的?可以先用 showdemowindow 找到想要的功能。

84. Properties Panel Game Engine series 85. Camera Component UI 86. Drawing Component UI

理论上我们最好有一个材质系统,比如我们的组件就可以这样:

我们现在基本上都是这样写的:

if (entity.HasComponent())
{
    if (ImGui::TreeNodeEx((void*)typeid(SpriteRendererComponent).hash_code(), ImGuiTreeNodeFlags_DefaultOpen, "Sprite Renderer"))
    {
        auto& src = entity.GetComponent();
        ImGui::ColorEdit4("Color", glm::value_ptr(src.Color));
        ImGui::TreePop();
    }
}

对于任何component都是这样去写,很重复。

未来我们会改为一个模板的形式,每次只需要:

这样,给一个具体的component的模板参数,然后给一个名字,再传入一个我们自定义的lambda的ui方法。而比如上面的 SpriteRendererComponent 代码在这里的lambda函数内部就只需要写这两行:

auto& src = entity.GetComponent();
ImGui::ColorEdit4("Color", glm::value_ptr(src.Color));

87. Transform Component UI

imgui 有一个 pushid 的功能,就像不同的 namespace 一样,否则我们这样的代码ImGui::DragFloat("##X", &values.x, 0.1f);,会由于都是 ##X 而导致 translation rotation 和 scale 操作相同的 bug。

88. Adding Removing Entities and Components UI 89. Making the Hazelnut Editor Look Good!
if (ImGui::InputText("##Tag", buffer, sizeof(buffer))) 

貌似 imgui 中这里的 ## 是隐藏的意思?

我们在这里这样改 docking 窗口的最小的宽度:

这里我设置的和 cherno 不一样,我设的是 470.0f,因为我发现我的情况只有这个值才能全装得下。

90. Saving and Loading Scenes!

这里我们希望是人可读的,所以放弃二进制,而是序列化成一个 txt 的形式。

这里我们没有选择 json 而是 yaml:
https://www.runoob.com/w3cnote/yaml-intro.html

这里注意 yaml 要使用静态库的方式,需要添加预定义 YAML_CPP_STATIC_DEFINE,这是 cherno 的yaml的premake没有做的一点。

并且还需要把这个宏加在 HEngine 的 project 里:

这里我们希望 yaml 可以重载我们的形式,我们这样写:

YAML::Emitter& operator<<(YAML::Emitter& out, const glm::vec3& v)
{
	out << YAML::Flow;
	out << YAML::BeginSeq << v.x << v.y << v.z << YAML::EndSeq;
	return out;
}

YAML::Emitter& operator<<(YAML::Emitter& out, const glm::vec4& v)
{
	out << YAML::Flow;
	out << YAML::BeginSeq << v.x << v.y << v.z << v.w << YAML::EndSeq;
	return out;
}

这里 YAML::Flow 可以让格式变得更直观。

91. Open Save File Dialogs 92. Transformation Gizmos 93. Editor Camera 94. Multiple Render Targets and Framebuffer Refactor

我们想要的效果如下:

FramebufferSpecification fbSpec;
fbSpec.Attachments = { FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::Depth };
fbSpec.Width = 1280;
fbSpec.Height = 720;
m_Framebuffer = Framebuffer::Create(fbSpec);

就是可以像这样通过一个描述,比如上面的 { FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::Depth },从而我们理应知道有两个 color buffer 和一个 depth buffer

做法很简单,首先封装一下:

enum class FramebufferTextureFormat
{
	None = 0, 

	// Color
	RGBA8,

	// Depth/stencil
	DEPTH24STENCIL8,

	// Defaults
	Depth = DEPTH24STENCIL8
};

struct FramebufferTextureSpecification
{
	FramebufferTextureSpecification() = default;
	FramebufferTextureSpecification(FramebufferTextureFormat format)
		: TextureFormat(format) {}

	FramebufferTextureFormat TextureFormat = FramebufferTextureFormat::None;
	// TODO: filtering/wrap
};

struct FramebufferAttachmentSpecification
{
	FramebufferAttachmentSpecification() = default;
	FramebufferAttachmentSpecification(std::initializer_list attachments)
		: Attachments(attachments) {}

	std::vector Attachments;
};

   struct FramebufferSpecification
   {
       uint32_t Width, Height;
	FramebufferAttachmentSpecification Attachments;
       uint32_t Samples = 1;

       bool SwapChainTarget = false;
   };

但是我个人感觉这样封装的层太多了,与其封装一个 vector 还不如直接封装一个 initializer_list 或者 vector,比如这样:

enum class FramebufferTextureFormat
{
	None = 0, 

	// Color
	RGBA8,

	// Depth/stencil
	DEPTH24STENCIL8,

	// Defaults
	Depth = DEPTH24STENCIL8
};

struct FramebufferTextureSpecification
{
	FramebufferTextureSpecification() = default;
	FramebufferTextureSpecification(FramebufferTextureFormat format)
		: TextureFormat(format) {}

	FramebufferTextureFormat TextureFormat = FramebufferTextureFormat::None;
	// TODO: filtering/wrap
};

   struct FramebufferSpecification
   {
       uint32_t Width, Height;
	std::initializer_list Attachments;
       uint32_t Samples = 1;

       bool SwapChainTarget = false;
   };

这样少一层封装我个人感觉清爽一些,使用的时候我们只需要这样填充:

OpenGLFramebuffer::OpenGLFramebuffer(const FramebufferSpecification& spec)
    : m_Specification(spec)
{
	for (auto spec : m_Specification.Attachments)
	{
		if (!Utils::IsDepthFormat(spec.TextureFormat))
			m_ColorAttachmentSpecifications.emplace_back(spec);
		else
			m_DepthAttachmentSpecification = spec;
	}

    Invalidate();
}

即在对应的图形 api 初始化的时候就去判断一下,每个描述是 color buffer 还是 depth buffer,然后通过类的内部成员:

std::vector m_ColorAttachmentSpecifications;
FramebufferTextureSpecification m_DepthAttachmentSpecification;

std::vector m_ColorAttachments;
uint32_t m_DepthAttachment = 0;

调api对应一下就好了。

不过目前先按着 cherno 的版本来好了,未来或许我会重构一下。

95. Preparing Framebuffers for Mouse Picking

我们希望 Multiple Render Targets 的第二个渲染目标可以返回每个 pixel 对应的 entity id。我们可以用 uint,但是 cherno 喜欢让 -1 为 非entity 的情况:

layout(location = 0) out vec4 color;
layout(location = 1) out int color2;

(当然我们仍然可以用 uint,习惯不同罢了)

96. Clearing Framebuffer Texture Attachments + Git Branching 97. Mouse Picking 98. Clicking to Select Entities

这里的原理是通过多重渲染目标(Multiple Render Targets),每次都把 entity id 给传入到着色器uniform,然后让第二个 framebuffer 的值就为那个 entity id

然后每次都去读这个 framebuffer,写函数:

int OpenGLFramebuffer::ReadPixel(uint32_t attachmentIndex, int x, int y)
{
	HE_CORE_ASSERT(attachmentIndex < m_ColorAttachments.size());

	glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentIndex);
	int pixelData;
	glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &pixelData);
	return pixelData;
}

传入 x y 为鼠标在对应视图的位置即可。

99. SPIR-V and the New Shader System

我们未来想支持 vulkan,但是虽然都是glsl,然而 vulkan 的 glsl 和 OpenGL 的 glsl 有一些差别,比如uniform

参考:
https://blog.csdn.net/qwertyu1234/article/details/50163847
https://www.khronos.org/spir/

SPIR-V是以一种二进制格式存在的,并且函数还是以控制流图CFG的形式存在;数据结构也保留了高级语言里的层级关系。
这样做的目的是为了更好的在目标平台上进行优化;同时Khronos也放出了官方标准的开源编译器Glslang。



按 cherno 的意思,貌似可以先编译成中间字节码 spir-v,再告诉OpenGL绑定这个spir-v。于是 vulkan 和 OpenGL 就都可以按照 vulkan 的glsl方式去写,然后都先编译成 spir-v,我看games104貌似也是用的spir-v:

ue4 shader 系统补充:
https://zhuanlan.zhihu.com/p/462659745

https://zhuanlan.zhihu.com/p/49069689

注:

上面对应的 YouTube 集数少一集,也就是 99 对应 YouTube 的 100 集

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/901074.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号