赞
踩
这里我们会想从一个 shader file 来编译一个 shader。
我们希望最终的 api 可以设计成这样:
Shader::Create("assets/shaders/Texture.glsl");
当然未来最好的是可以把 glsl 转换为我们自己的 shader language
当有一些示例shader比如pbr shader之类的,我们就可以存在引擎的 Shader Library 中。本质上就是用一个哈希表去存:
std::unordered_map<std::string, Ref<Shader>> m_Shaders;
对于2D的情况,最简单的就是所有的都看成一个 quad
比如画一个圆,最好的方式不是画边,而是简单地渲染一个 texture(带alpha通道的那种)。
为了避免混淆,我们命名为 HEngine::Renderer2D 这样的:
可以参考:
https://blog.csdn.net/alexhu2010q/article/details/122871987
对于 resize,我们不只是告诉window,我们还需要告诉我们的图形api,我们的渲染区域改变了。
因此这里我们需要 glviewport
当 window minimize的时候 width 和 height 是0,可以打个断点调试一下。所以我们还需要进行一些边界处理。
我们可以在 OnEvent 函数中自己定制想要的缩放效果:
void OnEvent(HEngine::Event& e) override
{
m_CameraController.OnEvent(e);
if (e.GetEventType() == HEngine::EventType::WindowResize)
{
auto& re = (HEngine::WindowResizeEvent&)e;
//float zoom = (float)re.GetWidth() / 1280.0f;
//m_CameraController.SetZoomLevel(zoom);
}
}
确保你的所有configuration都能够build,所以这次维护修改升级我们还需要build一下release版本来试试。
目前的Profiling用ImGUI绘制出来了,如上图所示,这种很简陋,缺点有:
参考链接:
https://blog.csdn.net/alexhu2010q/article/details/122871987
为了方便预览和分析程序里想要分析的代码片段每帧所花的时间,这里选了一个很简单的方法,就是在Runtime把Profile信息写到一个JSON文件里,然后利用Chrome提供的Chrome Tracing工具(只要在谷歌浏览器里输入chrome://tracing即可打开它),来帮忙分析这个JSON文件。
实现写入JSON文件的类叫instrumentor,这个单词在英语里其实并不存在,它源自于单词instrumentation,本意是一套仪器、仪表,在CS里的意思是对程序性能、错误等方面的监测:
In the context of computer programming, instrumentation refers to the measure of a product’s performance, to diagnose errors, and to write trace information.[1] Instrumentation can be of two types: source instrumentation and binary instrumentation.
我们生成好 json 文件后,在谷歌浏览器中进入 chrome://tracing
随后我们 load 文件 HEngineProfile-Runtime.json 进来,就可以看到这样的画面:
注:每次我都是先 F5 运行,然后先关我们的窗口,再关闭我们的console,否则会json解析失败?暂不清楚原因。
现在的Profiling系统,有个比较大的问题:
这一节写了一些记录效率的,比如 draw call 有多少个之类的,通过 imgui 渲染出来。
这一节上了一个 particle system,用的 cherno 这里的代码:
https://github.com/TheCherno/OneHourParticleSystem
其实每个 particle 就是一个 quad,我们只不过是写了一个 particle system 类来定义它每个时刻的位置、大小、旋转和颜色罢了。给一个生命周期比如一秒钟,然后大小依次递减即可,借助之前的 Timestep 就很容易做到。
最后通过我们的粒子池和index来发射粒子:
std::vector<Particle> m_ParticlePool;
uint32_t m_PoolIndex;
for (int i = 0; i < 50; i++)
m_ParticleSystem.Emit(m_Particle);
void ParticleSystem::Emit(const ParticleProps& particleProps) { Particle& particle = m_ParticlePool[m_PoolIndex]; particle.Active = true; particle.Position = particleProps.Position; particle.Rotation = Random::Float() * 2.0f * glm::pi<float>(); // Velocity particle.Velocity = particleProps.Velocity; particle.Velocity.x += particleProps.VelocityVariation.x * (Random::Float() - 0.5f); particle.Velocity.y += particleProps.VelocityVariation.y * (Random::Float() - 0.5f); // Color particle.ColorBegin = particleProps.ColorBegin; particle.ColorEnd = particleProps.ColorEnd; particle.LifeTime = particleProps.LifeTime; particle.LifeRemaining = particleProps.LifeTime; particle.SizeBegin = particleProps.SizeBegin + particleProps.SizeVariation * (Random::Float() - 0.5f); particle.SizeEnd = particleProps.SizeEnd; m_PoolIndex = --m_PoolIndex % m_ParticlePool.size(); }
资源:https://kenney.nl/assets/rpg-base
因为至多就 32 个slot,所以比如有 128 张 texture,那么至少就要 4 个 draw call
cherno 表示draw call不是问题,主要是 texture 每次都需要 bind 和 rebind 。因此打图集效率是很高的。
所以似乎 spritesheet 就是 texture atlas?
我们想做的是搞个 framebuffer,绘制到一张 texture 上,然后就可以用 imgui 的 render image 去绘制了。
比如:
uint32_t textureID = m_CheckerboardTexture->GetRendererID();
ImGui::Image((void*)textureID, ImVec2{ 256.0, 256.0 });
具体工作要在下一节再做了。
这里的 bool SwapChainTarget 就是是否是直接渲染到屏幕的意思(是不是GPU端的framebuffer?)
但是这一节发现了一个怪事,就是相机的上下颠倒了,YouTube评论区给出了原因:
If anyone else is having their images rendered upside down in ImGui, add ImVec2{ 0,1 }, ImVec2{1,0} to the parameters of ImGui::Image to change the UV’s of the image. For some reason, ImGui flips them by default
原来是 imgui 的自动处理,我们更改需要在后面加上两个参数 ImVec2{ 0, 1 }, ImVec2{ 1, 0 }:
ImGui::Image((void*)textureID, ImVec2{ 1280, 720 }, ImVec2{ 0, 1 }, ImVec2{ 1, 0 });
这里注意几点:
所以参考cherno后续的代码:
https://github.com/TheCherno/Hazel/blob/master/Hazel/src/Hazel/Core/Input.h
不如直接在 HEngine/HEngine/src/Platform/Windows/WindowsInput.cpp 中直接包含头文件 #include "HEngine/Core/Input.h"
,而不需要 WindowsInput.h
对于 ImGui Layer Events,我们先做如下测试:
我们通过这两行来测试:
HE_CORE_WARN("Focused: {0}", ImGui::IsWindowFocused());
HE_CORE_WARN("Hovered: {1}", ImGui::IsWindowHovered());
发现 hovered 是看鼠标有没有移动到那个窗口,而focesed就是看是不是选中了那个窗口了,很容易测试。
于是这就可以成为我们是否接收事件的条件判断。
注意我们的 Application 的构造函数中就会 PushOverlay(m_ImGuiLayer)
而我们对事件的检测是从后向前的:
void Application::OnEvent(Event& e)
{
EventDispatcher dispatcher(e);
dispatcher.Dispatch<WindowCloseEvent>(HE_BIND_EVENT_FN(Application::OnWindowClose));
dispatcher.Dispatch<WindowResizeEvent>(HE_BIND_EVENT_FN(Application::OnWindowResize));
for (auto it = m_LayerStack.rbegin(); it != m_LayerStack.rend(); ++it)
{
if (e.Handled)
break;
(*it)->OnEvent(e);
}
}
我们目前一共就两个层:一个是正常push进去的EditorLayer,一个是构造函数里头默认就会push进去的ImGuiLayer,且是overlay,也就是默认在最后面,因此每次事件都是先 imgui 判断一次,再传播到前面的层。
于是我们在 ImGuiLayer 中新增了一些方法判断:
void BlockEvents(bool block) { m_BlockEvents = block; }
void ImGuiLayer::OnEvent(Event& e)
{
if (m_BlockEvents)
{
ImGuiIO& io = ImGui::GetIO();
e.Handled |= e.IsInCategory(EventCategoryMouse) & io.WantCaptureMouse;
e.Handled |= e.IsInCategory(EventCategoryKeyboard) & io.WantCaptureKeyboard;
}
}
这样我们就可以通过一些设置来判断是否要传播到前面的层了!
比如:
EditorLayer.cpp 中:
m_ViewportFocused = ImGui::IsWindowFocused();
m_ViewportHovered = ImGui::IsWindowHovered();
Application::Get().GetImGuiLayer()->BlockEvents(!m_ViewportFocused || !m_ViewportHovered);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。