add auto exposure

This commit is contained in:
2026-02-26 18:17:12 +08:00
parent 99bbf1eb5a
commit 56da5ebef7
17 changed files with 909 additions and 807 deletions

View File

@ -16,7 +16,7 @@ file(GLOB_RECURSE SRC_SOURCE ./**.cpp)
add_executable(${PROJECT_NAME} ${SRC_SOURCE})
target_link_libraries(${PROJECT_NAME} PRIVATE Prism-shared)
target_link_libraries(${PROJECT_NAME} PRIVATE Prism-static)
# Enable ImGui Docking space
target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_DOCKSPACE)

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
#define EDITORLAYER_H
#include "Prism.h"
#include "Prism/Editor/AssetsManagerPanel.h"
#include "Prism/Editor/ContentBrowserPanel.h"
#include "Prism/Editor/ObjectsPanel.h"
#include "Prism/Editor/SceneHierachyPanel.h"
@ -58,7 +58,7 @@ namespace Prism
float GetSnapValue() const;
private:
Scope<SceneHierarchyPanel> m_SceneHierarchyPanel;
Scope<AssetsManagerPanel> m_AssetManagerPanel;
Scope<ContentBrowserPanel> m_ContentBrowserPanel;
Scope<ObjectsPanel> m_ObjectsPanel;
Ref<Scene> m_CurrentScene;
@ -133,7 +133,7 @@ namespace Prism
// Editor resources
Ref<Texture2D> m_CheckerboardTex;
Ref<Texture2D> m_PlayButtonTex;
Ref<Texture2D> m_PlayButtonTex, m_StopButtonTex, m_PauseButtonTex;
// configure button

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

View File

@ -1,6 +1,6 @@
[Window][DockSpace Demo]
Pos=0,0
Size=2066,1198
Size=2560,1566
Collapsed=0
[Window][Debug##Default]
@ -9,32 +9,32 @@ Size=400,400
Collapsed=0
[Window][Scene Hierarchy]
Pos=1595,24
Size=471,428
Pos=2089,24
Size=471,563
Collapsed=0
DockId=0x00000009,0
[Window][Properties]
Pos=1595,454
Size=471,744
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,0
[Window][Scene Renderer]
Pos=0,648
Size=481,550
Pos=0,843
Size=481,723
Collapsed=0
DockId=0x00000006,0
[Window][Materials]
Pos=0,24
Size=481,622
Size=481,817
Collapsed=0
DockId=0x00000005,0
[Window][Script Engine Debug]
Pos=1595,454
Size=471,744
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,2
@ -52,25 +52,25 @@ DockId=0x00000001,0
[Window][Viewport]
Pos=483,58
Size=1110,722
Size=1604,955
Collapsed=0
DockId=0x0000000B,0
[Window][Environment]
Pos=1595,454
Size=471,744
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,1
[Window][Project]
Pos=483,782
Size=1110,416
Pos=483,1015
Size=1604,551
Collapsed=0
DockId=0x0000000C,0
[Window][Objects]
Pos=483,782
Size=1110,416
Pos=483,1015
Size=1604,551
Collapsed=0
DockId=0x0000000C,1
@ -79,17 +79,23 @@ Pos=189,113
Size=468,371
Collapsed=0
[Window][##tool_bar]
Pos=483,24
Size=1604,32
Collapsed=0
DockId=0x00000001,0
[Docking][Data]
DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=150,89 Size=2066,1174 Split=X Selected=0x0C01D6D5
DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=0,58 Size=2560,1542 Split=X Selected=0x0C01D6D5
DockNode ID=0x00000007 Parent=0xC0DFADC4 SizeRef=1557,1542 Split=X
DockNode ID=0x00000003 Parent=0x00000007 SizeRef=481,1542 Split=Y Selected=0x5D711C2C
DockNode ID=0x00000005 Parent=0x00000003 SizeRef=481,817 Selected=0x5D711C2C
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=481,723 Selected=0x68D924E0
DockNode ID=0x00000004 Parent=0x00000007 SizeRef=1074,1542 Split=Y
DockNode ID=0x00000001 Parent=0x00000004 SizeRef=2560,32 CentralNode=1 HiddenTabBar=1 Selected=0x0C01D6D5
DockNode ID=0x00000001 Parent=0x00000004 SizeRef=2560,32 CentralNode=1 HiddenTabBar=1 Selected=0xE8CD5B84
DockNode ID=0x00000002 Parent=0x00000004 SizeRef=2560,1508 Split=Y Selected=0xC450F867
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1401,669 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,386 Selected=0x9C21DE82
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1401,955 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,551 Selected=0x9C21DE82
DockNode ID=0x00000008 Parent=0xC0DFADC4 SizeRef=471,1542 Split=Y Selected=0x8C72BEA8
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=315,563 Selected=0xB8729153
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=315,977 Selected=0x73E3D51F

View File

@ -66,7 +66,8 @@ elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
endif ()
include_directories(vendor/PhysX/physx/include)
link_directories("vendor/PhysX/physx/bin/win.x86_64.vc143.md/${PHYSX_BUILD_TYPE}") # This is the path where PhysX libraries are installed
set(PHYSX_LIB_DIR "vendor/PhysX/physx/bin/win.x86_64.vc143.md/${PHYSX_BUILD_TYPE}") # This is the path where PhysX libraries are installed
link_directories(${PHYSX_LIB_DIR})
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Building snippet in debug with PhysX ${PHYSX_BUILD_TYPE} configuration")
@ -139,6 +140,11 @@ target_compile_definitions(${STATIC_LIBRARY} PRIVATE
target_include_directories(${STATIC_LIBRARY} PUBLIC
${TARGET_INCLUDE_DIR}
)
target_link_directories(${STATIC_LIBRARY} INTERFACE
${PHYSX_LIB_DIR}
)
target_link_libraries(${STATIC_LIBRARY}
PRIVATE
${LINK_LIBRARIES_PRIVATE}

View File

@ -102,7 +102,6 @@ namespace Prism
{
m_ImGuiLayer->Begin();
/*
ImGui::Begin("Renderer");
const auto& caps = RendererAPI::GetCapabilities();
ImGui::Text("Vendor: %s", caps.Vendor.c_str());
@ -110,7 +109,6 @@ namespace Prism
ImGui::Text("Version: %s", caps.Version.c_str());
ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds());
ImGui::End();
*/
for (Layer* layer : m_LayerStack)
layer->OnImGuiRender();

View File

@ -0,0 +1,31 @@
//
// Created by Atdunbg on 2026/2/21.
//
#include "ImGui.h"
#include "Prism/Renderer/RendererAPI.h"
#include "Prism/Renderer/Texture.h"
namespace Prism::UI
{
void Image(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col)
{
ImGui::PushID(texture->Handle);
if (RendererAPI::Current() == RendererAPIType::OpenGL)
{
ImGui::Image((ImTextureID)texture->GetRendererID(), size, uv0, uv1, tint_col, border_col);
}
ImGui::PopID();
}
bool ImageButton(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col)
{
if (RendererAPI::Current() == RendererAPIType::OpenGL)
{
return ImGui::ImageButton(std::string_view("##" + std::to_string(texture->Handle)).data(), (ImTextureID)texture->GetRendererID(), size, uv0, uv1, bg_col, tint_col);
}
return false;
}
}

View File

@ -9,8 +9,13 @@
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "../../Asset/Asset.h"
#include "../../Asset/AssetsManager.h"
#include "Prism/Asset/Asset.h"
#include "Prism/Asset/AssetsManager.h"
namespace Prism
{
class Texture2D;
}
namespace Prism::UI {
@ -259,6 +264,157 @@ namespace Prism::UI {
return modified;
}
static bool PropertySlider(const char* label, float& value, const float min, const float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat(s_IDBuffer, &value, min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, glm::vec2& value, const float min, const float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat2(s_IDBuffer, glm::value_ptr(value), min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, glm::vec3& value, const float min, const float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat3(s_IDBuffer, glm::value_ptr(value), min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertySlider(const char* label, glm::vec4& value, float min, float max)
{
bool modified = false;
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
s_IDBuffer[0] = '#';
s_IDBuffer[1] = '#';
memset(s_IDBuffer + 2, 0, 14);
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
if (ImGui::SliderFloat4(s_IDBuffer, glm::value_ptr(value), min, max))
modified = true;
ImGui::PopItemWidth();
ImGui::NextColumn();
return modified;
}
static bool PropertyDropdown(const char* label, const char** options, int32_t optionCount, int32_t* selected)
{
const char* current = options[*selected];
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
bool changed = false;
const std::string id = "##" + std::string(label);
if (ImGui::BeginCombo(id.c_str(), current))
{
for (int i = 0; i < optionCount; i++)
{
const bool is_selected = (current == options[i]);
if (ImGui::Selectable(options[i], is_selected))
{
current = options[i];
*selected = i;
changed = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::NextColumn();
return changed;
}
static bool PropertyDropdown(const char* label, const std::vector<std::string>& options, int32_t optionCount, int32_t* selected)
{
const char* current = options[*selected].c_str();
ImGui::Text("%s", label);
ImGui::NextColumn();
ImGui::PushItemWidth(-1);
bool changed = false;
const std::string id = "##" + std::string(label);
if (ImGui::BeginCombo(id.c_str(), current))
{
for (int i = 0; i < optionCount; i++)
{
const bool is_selected = (current == options[i]);
if (ImGui::Selectable(options[i].c_str(), is_selected))
{
current = options[i].c_str();
*selected = i;
changed = true;
}
if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::PopItemWidth();
ImGui::NextColumn();
return changed;
}
template<typename T>
static bool PropertyAssetReference(const char* label, Ref<T>& object, const AssetType supportedType)
{
@ -353,6 +509,10 @@ namespace Prism::UI {
s_CheckboxCount = 0;
}
void Image(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col = ImVec4(0,0,0,0), const ImVec4& border_col = ImVec4(1,1,1,1));
bool ImageButton(const Ref<Texture2D>& texture, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1));
}
#endif //IMGUI_H

View File

@ -2,18 +2,19 @@
// Created by Atdunbg on 2026/1/20.
//
#include "AssetsManagerPanel.h"
#include "ContentBrowserPanel.h"
#include <filesystem>
#include "AssetEditorPanel.h"
#include "imgui_internal.h"
#include "Prism/Core/Application.h"
#include "Prism/Core/Input.h"
#include "Prism/Core/Log.h"
namespace Prism
{
AssetsManagerPanel::AssetsManagerPanel()
ContentBrowserPanel::ContentBrowserPanel()
{
AssetsManager::SetAssetChangeCallback([&]()
{
@ -47,7 +48,7 @@ namespace Prism
static int s_ColumnCount = 10;
void AssetsManagerPanel::OnImGuiRender()
void ContentBrowserPanel::OnImGuiRender()
{
ImGui::Begin("Project", nullptr, ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar);
{
@ -73,7 +74,6 @@ namespace Prism
// Grid and list files
ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetWindowHeight() - 60));
// ImGui::BeginChild("##directory_structure", ImVec2(ImGui::GetColumnWidth() - 12, ImGui::GetContentRegionAvail().y));
{
ImGui::BeginChild("##directory_breadcrumbs", ImVec2(ImGui::GetColumnWidth() - 100, 30));
RenderBreadCrumbs();
@ -195,26 +195,51 @@ namespace Prism
ImGui::End();
}
void AssetsManagerPanel::DrawDirectoryInfo(const AssetHandle& directory)
void ContentBrowserPanel::DrawDirectoryInfo(const AssetHandle& directory)
{
const Ref<Directory>& dir = AssetsManager::GetAsset<Directory>(directory);
if (ImGui::TreeNode(dir->FileName.c_str()))
{
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
UpdateCurrentDirectory(directory);
ImGui::PushID((int)directory);
for (AssetHandle child : dir->ChildDirectories)
// 保存调用 TreeNodeEx 前的光标位置(箭头起始位置)
const ImVec2 arrow_pos = ImGui::GetCursorScreenPos();
// 设置标志:仅箭头点击展开,双击文本也可展开(可选)
constexpr ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick;
const bool node_open = ImGui::TreeNodeEx(dir->FileName.c_str(), flags, "%s", dir->FileName.c_str());
const ImRect item_rect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
// 计算箭头区域矩形(宽度为字体大小,高度与项一致)
const float arrow_width = ImGui::GetFontSize();
const ImRect arrow_rect(arrow_pos, ImVec2(arrow_pos.x + arrow_width, item_rect.Max.y));
// 检测左键单击(排除双击)
if (ImGui::IsItemHovered() && ImGui::IsMouseClicked(ImGuiMouseButton_Left) && !ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
{
const ImVec2 mouse_pos = ImGui::GetMousePos();
// 仅当点击位置不在箭头区域内时,才触发选择
if (!arrow_rect.Contains(mouse_pos))
{
UpdateCurrentDirectory(directory);
}
}
// 如果节点打开,绘制子文件夹
if (node_open)
{
for (const AssetHandle& child : dir->ChildDirectories)
{
DrawDirectoryInfo(child);
}
ImGui::TreePop();
ImGui::TreePop(); // 必须与 TreeNodeEx 匹配
}
ImGui::PopID();
}
void AssetsManagerPanel::RenderAsset(Ref<Asset>& asset)
void ContentBrowserPanel::RenderAsset(Ref<Asset>& asset)
{
// These caches are currently required for when we change directories
const AssetHandle assetHandle = asset->Handle;
@ -345,7 +370,7 @@ namespace Prism
ImGui::PopID();
}
void AssetsManagerPanel::HandleDragDrop(const RendererID icon, const Ref<Asset>& asset)
void ContentBrowserPanel::HandleDragDrop(const RendererID icon, const Ref<Asset>& asset)
{
if (asset->Type == AssetType::Directory && m_IsDragging)
{
@ -392,7 +417,7 @@ namespace Prism
}
void AssetsManagerPanel::RenderBreadCrumbs()
void ContentBrowserPanel::RenderBreadCrumbs()
{
if (ImGui::ImageButton("##backbtn", (ImTextureID)m_BackbtnTex->GetRendererID(), ImVec2(20, 18)))
{
@ -475,7 +500,7 @@ namespace Prism
}
void AssetsManagerPanel::HandleRenaming(Ref<Asset>& asset)
void ContentBrowserPanel::HandleRenaming(Ref<Asset>& asset)
{
if (m_SelectedAssets.SelectionCount() > 1)
return;
@ -502,7 +527,7 @@ namespace Prism
}
void AssetsManagerPanel::UpdateCurrentDirectory(AssetHandle directoryHandle)
void ContentBrowserPanel::UpdateCurrentDirectory(AssetHandle directoryHandle)
{
m_UpdateBreadCrumbs = true;

View File

@ -2,8 +2,8 @@
// Created by Atdunbg on 2026/1/20.
//
#ifndef PRISM_ASSETSMANAGERPANEL_H
#define PRISM_ASSETSMANAGERPANEL_H
#ifndef PRISM_CONTENTBROWSERPANEL_H
#define PRISM_CONTENTBROWSERPANEL_H
#include "Prism/Asset/AssetsManager.h"
#include "Prism/Renderer/Texture.h"
@ -64,10 +64,10 @@ namespace Prism
std::vector<T> m_Selections;
};
class PRISM_API AssetsManagerPanel
class PRISM_API ContentBrowserPanel
{
public:
AssetsManagerPanel();
ContentBrowserPanel();
void OnImGuiRender();
private:
@ -116,4 +116,4 @@ namespace Prism
};
}
#endif //PRISM_ASSETSMANAGERPANEL_H
#endif //PRISM_CONTENTBROWSERPANEL_H

View File

@ -217,109 +217,6 @@ namespace Prism
glDrawBuffer(GL_NONE);
}
#if 0
if (multisample)
{
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_ColorAttachment);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment);
// TODO: Create Hazel texture object based on format here
if (m_Specification.Format == FramebufferFormat::RGBA16F)
{
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA16F, m_Specification.Width, m_Specification.Height, GL_FALSE);
}
else if (m_Specification.Format == FramebufferFormat::RGBA8)
{
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_RGBA8, m_Specification.Width, m_Specification.Height, GL_FALSE);
}
// glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
}
else
{
glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment);
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment);
// TODO: Create Hazel texture object based on format here
if (m_Specification.Format == FramebufferFormat::RGBA16F)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
}
else if (m_Specification.Format == FramebufferFormat::RG32F) // "Shadow" for now
{
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH_COMPONENT32F, m_Specification.Width, m_Specification.Height);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_ColorAttachment, 0);
glDrawBuffer(GL_NONE);
}
else if (m_Specification.Format == FramebufferFormat::RGBA8)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
}
else if (m_Specification.Format == FramebufferFormat::COMP)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glCreateTextures(GL_TEXTURE_2D, 1, &m_ColorAttachment2);
glBindTexture(GL_TEXTURE_2D, m_ColorAttachment2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, m_Specification.Width, m_Specification.Height, 0, GL_RGBA, GL_FLOAT, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
if (m_Specification.Format != FramebufferFormat::RG32F)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_ColorAttachment, 0);
}
}
if (multisample)
{
glCreateTextures(GL_TEXTURE_2D_MULTISAMPLE, 1, &m_DepthAttachment);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, m_DepthAttachment);
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, m_Specification.Samples, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, GL_FALSE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
}
else if (m_Specification.Format != FramebufferFormat::RG32F)
{
glCreateTextures(GL_TEXTURE_2D, 1, &m_DepthAttachment);
glBindTexture(GL_TEXTURE_2D, m_DepthAttachment);
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_Specification.Width, m_Specification.Height, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, m_DepthAttachment, 0);
}
if (m_Specification.Format != FramebufferFormat::RG32F)
{
if (multisample)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, m_ColorAttachment, 0);
}
else
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_ColorAttachment, 0);
if (m_Specification.Format == FramebufferFormat::COMP)
{
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_ColorAttachment2, 0);
const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, buffers);
}
}
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_DepthAttachment, 0);
}
#endif
PM_CORE_ASSERT(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Framebuffer is incomplete!");
glBindFramebuffer(GL_FRAMEBUFFER, 0);

View File

@ -154,6 +154,13 @@ namespace Prism
UploadUniformInt(name, value);
});
}
void OpenGLShader::SetUInt(const std::string& name, unsigned int value)
{
Renderer::Submit([=]() {
UploadUniformUInt(name, value);
});
}
void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value)
{
@ -709,16 +716,36 @@ namespace Prism
return GL_NONE;
}
void OpenGLShader::UploadUniformBool(const std::string& name, const bool value) const
{
glUseProgram(m_RendererID);
const auto location = glGetUniformLocation(m_RendererID, name.c_str());
if (location != -1)
glUniform1i(location, value);
else
PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
}
void OpenGLShader::UploadUniformInt(const std::string& name, const int value) const
{
glUseProgram(m_RendererID);
auto location = glGetUniformLocation(m_RendererID, name.c_str());
const auto location = glGetUniformLocation(m_RendererID, name.c_str());
if (location != -1)
glUniform1i(location, value);
else
PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
}
void OpenGLShader::UploadUniformUInt(const std::string& name, int value) const
{
glUseProgram(m_RendererID);
const auto location = glGetUniformLocation(m_RendererID, name.c_str());
if (location != -1)
glUniform1ui(location, value);
else
PM_CORE_WARN("{0}: Uniform '{1}' not found!", m_Name,name);
}
void OpenGLShader::UploadUniformFloat(const std::string& name, const float value) const
{
glUseProgram(m_RendererID);

View File

@ -32,6 +32,7 @@ namespace Prism
virtual void SetFloat2(const std::string& name, const glm::vec2& value) override;
virtual void SetFloat3(const std::string& name, const glm::vec3& value) override;
virtual void SetInt(const std::string& name, int value) override;
virtual void SetUInt(const std::string& name, unsigned int value) override;
virtual void SetMat4(const std::string& name, const glm::mat4& value) override;
virtual void SetIntArray(const std::string& name, int* values, uint32_t size) override;
@ -43,7 +44,6 @@ namespace Prism
const std::string& GetName() const override;
private:
void Load(const std::string& source);
@ -64,7 +64,9 @@ namespace Prism
static GLenum ShaderTypeFromString(const std::string& type);
void UploadUniformBool(const std::string& name, bool value) const;
void UploadUniformInt(const std::string& name, int value) const;
void UploadUniformUInt(const std::string& name, int value) const;
void UploadUniformFloat(const std::string& name, float value) const;
void UploadUniformFloat2(const std::string& name, const glm::vec2& values) const;
void UploadUniformFloat3(const std::string& name, const glm::vec3& values) const;

View File

@ -13,6 +13,8 @@
#include "RenderPass.h"
#include "glad/glad.h"
#include "Prism/Core/Timer.h"
#include <cmath>
#include <algorithm>
namespace Prism
{
@ -40,6 +42,18 @@ namespace Prism
Ref<RenderPass> CompositePass;
Ref<RenderPass> BloomBlurPass[2];
Ref<RenderPass> BloomBlendPass;
Ref<RenderPass> LuminancePass;
// Auto exposure
struct AutoExposure
{
float CurrentExposure = 1.0f;
bool EnableAutoExposure = true;
float Key = 0.18f; // middle gray
float AdaptationSpeed = 1.0f; // stops per second
Timer ExposureTimer;
float MaxExposure = 5.0f;
}AutoExposure;
Ref<Shader> ShadowMapShader, ShadowMapAnimShader;
Ref<RenderPass> ShadowMapRenderPass[4];
@ -89,15 +103,104 @@ namespace Prism
float ShadowPass = 0.0f;
float GeometryPass = 0.0f;
float CompositePass = 0.0f;
float AutoExposurePass = 0.0f;
Timer ShadowPassTimer;
Timer GeometryPassTimer;
Timer CompositePassTimer;
Timer AutoExposurePassTimer;
};
static SceneRendererData s_Data;
static SceneRendererStats s_Stats;
static void ComputeAutoExposure()
{
if (!s_Data.AutoExposure.EnableAutoExposure)
return;
auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer;
auto dstFB = s_Data.LuminancePass->GetSpecification().TargetFramebuffer;
if (!srcFB || !dstFB)
return;
const uint32_t srcID = srcFB->GetColorAttachmentRendererID();
const uint32_t dstID = dstFB->GetColorAttachmentRendererID();
const uint32_t width = dstFB->GetWidth();
const uint32_t height = dstFB->GetHeight();
Renderer::Submit([srcID, dstID, width, height, srcFB]() mutable
{
// Use framebuffer blit to resolve multisampled source into non-multisampled luminance target.
GLuint srcFBO = srcFB->GetRendererID();
GLuint dstFBO = s_Data.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID();
// Bind read/draw FBOs and blit color attachment 0
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
// Source size — try to use srcFB dimensions if available
int srcWidth = srcFB->GetWidth();
int srcHeight = srcFB->GetHeight();
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Unbind
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
// Generate mipmaps so the smallest mip is the average color
glGenerateTextureMipmap(dstID);
// Determine highest mip level
int maxLevel = (int)std::floor(std::log2((float)std::max(width, height)));
if (maxLevel < 0) maxLevel = 0;
float pixel[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, sizeof(pixel), pixel);
// Sanitize pixel values (handle NaN/Inf or negative values coming from GPU)
for (int i = 0; i < 3; ++i)
{
if (!std::isfinite(pixel[i]) || pixel[i] < 0.0f)
pixel[i] = 0.0f;
}
// Convert to luminance
float lum = 0.2126f * pixel[0] + 0.7152f * pixel[1] + 0.0722f * pixel[2];
if (!std::isfinite(lum) || lum <= 0.0f)
lum = 1e-6f; // fallback minimum luminance
// Compute desired exposure (simple key/avg approach)
const float key = s_Data.AutoExposure.Key;
constexpr float minLum = 1e-6f;
float desiredExposure = key / std::max(lum, minLum);
desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposure.MaxExposure);
// Adapt exposure over time (exponential)
const float dt = s_Data.AutoExposure.ExposureTimer.ElapsedMillis() / 1000.0f;
s_Data.AutoExposure.ExposureTimer.Reset();
const float tau = s_Data.AutoExposure.AdaptationSpeed;
const float adaptFactor = 1.0f - std::exp(-tau * dt);
s_Data.AutoExposure.CurrentExposure = s_Data.AutoExposure.CurrentExposure + (desiredExposure - s_Data.AutoExposure.CurrentExposure) * adaptFactor;
s_Data.AutoExposure.CurrentExposure = std::clamp(s_Data.AutoExposure.CurrentExposure, 0.0001f, s_Data.AutoExposure.MaxExposure);
// Write exposure directly into composite shader program uniform so the subsequent composite pass uses it
/*
if (const GLuint prog = s_Data.CompositeShader->GetRendererID())
{
const GLint loc = glGetUniformLocation(prog, "u_Exposure");
if (loc >= 0)
glProgramUniform1f(prog, loc, s_Data.CurrentExposure);
}
*/
});
}
void SceneRenderer::Init()
{
FramebufferSpecification geoFramebufferSpec;
@ -140,6 +243,21 @@ namespace Prism
s_Data.BloomBlendShader = Shader::Create("assets/shaders/BloomBlend.glsl");
s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga");
// Luminance pass: used to compute average scene luminance (for auto exposure)
FramebufferSpecification luminanceFramebufferSpec;
luminanceFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F };
luminanceFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
RenderPassSpecification luminanceRenderPassSpec;
luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec);
s_Data.LuminancePass = RenderPass::Create(luminanceRenderPassSpec);
// Auto exposure defaults
s_Data.AutoExposure.CurrentExposure = 1.0f;
s_Data.AutoExposure.EnableAutoExposure = true;
s_Data.AutoExposure.Key = 0.18f;
s_Data.AutoExposure.AdaptationSpeed = 1.0f;
s_Data.AutoExposure.ExposureTimer.Reset();
// Grid
const auto gridShader = Shader::Create("assets/shaders/Grid.glsl");
@ -200,6 +318,8 @@ namespace Prism
{
s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height);
s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height);
if (s_Data.LuminancePass)
s_Data.LuminancePass->GetSpecification().TargetFramebuffer->Resize(width, height);
}
void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera)
@ -361,40 +481,29 @@ namespace Prism
memset(&s_Stats, 0, sizeof(SceneRendererStats));
{
Renderer::Submit([]()
{
s_Stats.ShadowPassTimer.Reset();
});
Renderer::Submit([]() { s_Stats.ShadowPassTimer.Reset(); });
ShadowMapPass();
Renderer::Submit([]
{
s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis();
});
Renderer::Submit([] { s_Stats.ShadowPass = s_Stats.ShadowPassTimer.ElapsedMillis(); });
}
{
Renderer::Submit([]()
{
s_Stats.GeometryPassTimer.Reset();
});
Renderer::Submit([]() { s_Stats.GeometryPassTimer.Reset(); });
GeometryPass();
Renderer::Submit([]
{
s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis();
});
Renderer::Submit([] { s_Stats.GeometryPass = s_Stats.GeometryPassTimer.ElapsedMillis(); });
}
// Compute average luminance and update exposure (GPU-copy + mipmap -> read 1 texel)
{
Renderer::Submit([]()
{
s_Stats.CompositePassTimer.Reset();
});
Renderer::Submit([]() { s_Stats.AutoExposurePassTimer.Reset(); });
ComputeAutoExposure();
Renderer::Submit([] { s_Stats.AutoExposurePass = s_Stats.AutoExposurePassTimer.ElapsedMillis(); });
}
{
Renderer::Submit([]() { s_Stats.CompositePassTimer.Reset(); });
CompositePass();
Renderer::Submit([]
{
s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis();
});
BloomBlurPass();
Renderer::Submit([] { s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis(); });
BloomBlurPass();
}
s_Data.DrawList.clear();
@ -684,7 +793,7 @@ namespace Prism
Renderer::BeginRenderPass(s_Data.CompositePass);
s_Data.CompositeShader->Bind();
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposure.EnableAutoExposure ? s_Data.AutoExposure.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
// s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight()));
// s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint);
@ -741,7 +850,8 @@ namespace Prism
Renderer::BeginRenderPass(s_Data.BloomBlendPass);
s_Data.BloomBlendShader->Bind();
auto & adad =s_Data;
s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
// s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposure.EnableAutoExposure ? s_Data.AutoExposure.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom);
s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0);
@ -796,7 +906,7 @@ namespace Prism
cascadeSplits[i] = (d - nearClip) / clipRange;
}
cascadeSplits[3] = 0.3f;
// cascadeSplits[3] = 0.3f;
// Manually set cascades here
// cascadeSplits[0] = 0.05f;
@ -994,6 +1104,20 @@ namespace Prism
UI::EndTreeNode();
}
if (UI::BeginTreeNode("Auto Exposure"))
{
UI::Property("Auto Exposure Pass time: %.2fs", s_Stats.AutoExposurePass);
UI::BeginPropertyGrid();
UI::Property("Enable Auto Exposure", s_Data.AutoExposure.EnableAutoExposure);
UI::Property("Key (middle gray)", s_Data.AutoExposure.Key, 0.001f, 0.001f, 2.5f);
UI::Property("Adaptation Speed", s_Data.AutoExposure.AdaptationSpeed, 0.01f, 0.001f, 5.0f);
UI::Property("Current Exposure", s_Data.AutoExposure.CurrentExposure, 0.01f, 0.0f, 0.0f, true);
UI::EndPropertyGrid();
UI::EndTreeNode();
}
ImGui::End();
}

View File

@ -32,6 +32,7 @@ namespace Prism
{
public:
static void Init();
void InitAutoExposure();
static void SetViewportSize(uint32_t width, uint32_t height);

View File

@ -111,6 +111,7 @@ namespace Prism
virtual void UploadUniformBuffer(const UniformBufferBase& uniformBuffer) = 0;
virtual void SetInt(const std::string& name, int value) = 0;
virtual void SetUInt(const std::string& name, unsigned int value) = 0;
virtual void SetBool(const std::string& name, bool value) = 0;
virtual void SetFloat(const std::string& name, float value) = 0;
virtual void SetFloat2(const std::string& name, const glm::vec2& value) = 0;