1 Commits

605 changed files with 6950 additions and 13911 deletions

3
.gitmodules vendored
View File

@ -35,6 +35,3 @@
[submodule "Prism/vendor/efsw"]
path = Prism/vendor/efsw
url = https://github.com/SpartanJ/efsw
[submodule "Prism/vendor/imgui-node-editor"]
path = Prism/vendor/imgui-node-editor
url = https://github.com/thedmd/imgui-node-editor.git

View File

@ -23,5 +23,4 @@ endif ()
add_subdirectory(Prism)
add_subdirectory(Sandbox)
add_subdirectory(Editor)
add_subdirectory(PrismRuntime)
add_subdirectory(Editor)

View File

@ -12,7 +12,7 @@ file(COPY ${IMGUI_INI} DESTINATION ${CMAKE_BINARY_DIR})
file(GLOB DOTNET_LIBRARY library)
file(COPY ${DOTNET_LIBRARY} DESTINATION ${CMAKE_BINARY_DIR})
file(GLOB_RECURSE SRC_SOURCE ./Editor/**.cpp)
file(GLOB_RECURSE SRC_SOURCE ./**.cpp)
add_executable(${PROJECT_NAME} ${SRC_SOURCE})

View File

@ -15,14 +15,10 @@
#include "Prism/Core/Input.h"
#include "Prism/Core/Math/Math.h"
#include "Prism/Editor/AssetEditorPanel.h"
#include "Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.h"
#include "Prism/Editor/PhysicsSettingsWindow.h"
#include "Prism/Asset/AssetsManager.h"
#include "Prism/Physics/Physics3D.h"
#include "Prism/Renderer/Renderer2D.h"
#include "Prism/Renderer/Renderer3D.h"
#include "Prism/Script/ScriptEngine.h"
#include "Prism/Script/ScriptWrappers.h"
namespace Prism
{
@ -55,18 +51,8 @@ namespace Prism
NewScene();
m_CurrentScene = m_EditorScene;
SceneRenderer::GetOptions().ShowGrid = true;
AssetEditorPanel::RegisterDefaultEditors();
PBRMaterialEditor::InitPreviewRenderer();
FileSystem::StartWatching();
m_ConsolePanel = CreateScope<ConsolePanel>();
Script::SetLogCallback([this](const std::string& message)
{
if (m_ConsolePanel)
m_ConsolePanel->AddMessage(message, ImVec4(1,1,1,1));
});
}
void EditorLayer::OnDetach()
@ -77,8 +63,6 @@ namespace Prism
void EditorLayer::OnUpdate(const TimeStep deltaTime)
{
FileSystem::ProcessPendingEvents();
auto [x, y] = GetMouseViewportSpace();
SceneRenderer::SetFocusPoint({ x * 0.5f + 0.5f, y * 0.5f + 0.5f });
@ -121,7 +105,7 @@ namespace Prism
Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false);
const auto viewProj = m_EditorCamera.GetViewProjection();
Renderer2D::BeginScene(viewProj, false);
Renderer2D::DrawRotatedRect({ transform.Translation.x + collider.Offset.x, transform.Translation.y + collider.Offset.y , transform.Translation.z}, {transform.Scale.x * collider.Size.x, transform.Scale.y * collider.Size.y}, transform.Rotation.z, { 0.0f, 0.0f, 1.0f, 1.0f });
Renderer2D::DrawRotatedRect({ transform.Translation.x + collider.Offset.x, transform.Translation.y + collider.Offset.y }, {transform.Scale.x * collider.Size.x, transform.Scale.y * collider.Size.y}, transform.Rotation.z, { 0.0f, 0.0f, 1.0f, 1.0f });
Renderer2D::EndScene();
Renderer::EndRenderPass();
}
@ -133,7 +117,7 @@ namespace Prism
Renderer::BeginRenderPass(SceneRenderer::GetFinalRenderPass(), false);
const auto viewProj = m_EditorCamera.GetViewProjection();
Renderer2D::BeginScene(viewProj, false);
Renderer2D::DrawCircle({ transform.Translation.x + collider.Offset.x, transform.Translation.y + collider.Offset.y, transform.Translation.z}, collider.Radius, { 0.0f, 1.0f, 1.0f, 1.0f });
Renderer2D::DrawCircle({ transform.Translation.x + collider.Offset.x, transform.Translation.y + collider.Offset.y}, collider.Radius, { 0.0f, 1.0f, 1.0f, 1.0f });
Renderer2D::EndScene();
Renderer::EndRenderPass();
}
@ -160,37 +144,6 @@ namespace Prism
}
}
if (m_SceneState == SceneState::Play)
{
if (Input::GetCursorMode() != CursorMode::Normal)
{
if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL) && Input::IsKeyPressed(KeyCode::LEFT_ALT) && Input::IsKeyPressed(KeyCode::GRAVE_ACCENT))
{
Input::SetCursorMode(CursorMode::Normal);
}
}
}
auto editingAsset = PBRMaterialEditor::GetEditingAsset();
if (editingAsset && (editingAsset->PreviewIsDirty || !editingAsset->PreviewTexture))
{
PBRMaterialEditor::RenderMaterialPreview(editingAsset);
}
else
{
bool renderedPreviewThisFrame = false;
AssetsManager::ForEachAsset<PBRMaterialAsset>([&](Ref<PBRMaterialAsset> mat)
{
if (renderedPreviewThisFrame) return;
if (mat->PreviewIsDirty || !mat->PreviewTexture)
{
PBRMaterialEditor::RenderMaterialPreview(mat);
renderedPreviewThisFrame = true;
}
});
}
}
void EditorLayer::OnImGuiRender()
@ -284,7 +237,7 @@ namespace Prism
{
// temp
if (ImGui::MenuItem("Reload C# Assembly"))
ScriptEngine::ReloadAssembly("assets/scripts/Assembly-Script.dll");
ScriptEngine::ReloadAssembly("assets/scripts/ExampleApp.dll");
ImGui::MenuItem("Reload assembly on play", nullptr, &m_ReloadScriptOnPlay);
ImGui::EndMenu();
@ -293,6 +246,7 @@ namespace Prism
if (ImGui::BeginMenu("Edit"))
{
ImGui::MenuItem("Physics Settings", nullptr, &m_ShowPhysicsSettings);
ImGui::EndMenu();
}
@ -308,9 +262,6 @@ namespace Prism
Physics3D::DisconnectPVD();
}
ImGui::Separator();
ImGui::MenuItem("GamePad View", nullptr, &m_ShowGamePadSettings);
ImGui::EndMenu();
}
@ -324,9 +275,8 @@ namespace Prism
Entity selectedEntity = m_SelectionContext.front().Entity;
if (selectedEntity.HasComponent<MeshComponent>())
{
auto& meshComponent = selectedEntity.GetComponent<MeshComponent>();
if (Ref<Mesh> mesh = meshComponent.Mesh)
Ref<Mesh> mesh = selectedEntity.GetComponent<MeshComponent>().Mesh;
if (mesh)
{
auto& materials = mesh->GetMaterials();
static uint32_t selectedMaterialIndex = 0;
@ -354,13 +304,27 @@ namespace Prism
}
ImGui::EndChild();
}
/*
for (uint32_t i = 0; i < materials.size(); i++)
{
const auto& materialInstance = materials[i];
ImGuiTreeNodeFlags node_flags = (selectedMaterialIndex == i ? ImGuiTreeNodeFlags_Selected : 0) | ImGuiTreeNodeFlags_Leaf;
bool opened = ImGui::TreeNodeEx((void*)(&materialInstance), node_flags, "%s", materialInstance->GetName().c_str());
if (ImGui::IsItemClicked())
{
selectedMaterialIndex = i;
}
if (opened)
ImGui::TreePop();
}
*/
ImGui::Separator();
if (selectedMaterialIndex < materials.size())
{
bool shouldUpdate = false;
auto& materialInstance = materials[selectedMaterialIndex];
ImGui::Text("Shader: %s", materialInstance->GetShader()->GetName().c_str());
// Textures ------------------------------------------------------------------------------
@ -370,26 +334,11 @@ namespace Prism
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
auto& albedoColor = materialInstance->Get<glm::vec4>("u_AlbedoColor");
auto& albedoColor = materialInstance->Get<glm::vec3>("u_AlbedoColor");
bool useAlbedoMap = materialInstance->Get<float>("u_AlbedoTexToggle");
Ref<Texture2D> albedoMap = materialInstance->TryGetResource<Texture2D>("u_AlbedoTexture");
ImGui::Image(albedoMap ? (ImTextureRef)albedoMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64));
if (ImGui::BeginDragDropTarget())
{
if (const auto data = ImGui::AcceptDragDropPayload("asset_payload"))
{
AssetHandle assetHandle = *(AssetHandle*)data->Data;
if (AssetsManager::IsAssetType(assetHandle, AssetType::Texture))
{
albedoMap = AssetsManager::GetAsset<Texture2D>(assetHandle);
shouldUpdate = true;
}
}
ImGui::EndDragDropTarget();
}
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
@ -402,23 +351,28 @@ namespace Prism
ImGui::Image((ImTextureRef)albedoMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty())
{
albedoMap = Texture2D::Create(filename, true/*m_AlbedoInput.SRGB*/);
materialInstance->Set("u_AlbedoTexture", albedoMap);
}
}
}
ImGui::SameLine();
ImGui::BeginGroup();
if (ImGui::Checkbox("Use##AlbedoMap", &useAlbedoMap))
{
materialInstance->Set<float>("u_AlbedoTexToggle", useAlbedoMap ? 1.0f : 0.0f);
shouldUpdate = true;
}
/*if (ImGui::Checkbox("sRGB##AlbedoMap", &m_AlbedoInput.SRGB))
{
if (m_AlbedoInput.TextureMap)
m_AlbedoInput.TextureMap = Texture2D::Create(m_AlbedoInput.TextureMap->GetPath(), m_AlbedoInput.SRGB);
}*/
ImGui::EndGroup();
ImGui::SameLine();
if (ImGui::ColorEdit4("Color##Albedo", glm::value_ptr(albedoColor), ImGuiColorEditFlags_NoInputs))
{
meshComponent.UpdateMaterials(selectedMaterialIndex);
shouldUpdate = true;
}
ImGui::ColorEdit3("Color##Albedo", glm::value_ptr(albedoColor), ImGuiColorEditFlags_NoInputs);
}
}
{
@ -431,21 +385,6 @@ namespace Prism
Ref<Texture2D> normalMap = materialInstance->TryGetResource<Texture2D>("u_NormalTexture");
ImGui::Image(normalMap ? (ImTextureRef)normalMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64));
if (ImGui::BeginDragDropTarget())
{
if (const auto data = ImGui::AcceptDragDropPayload("asset_payload"))
{
AssetHandle assetHandle = *(AssetHandle*)data->Data;
if (AssetsManager::IsAssetType(assetHandle, AssetType::Texture))
{
normalMap = AssetsManager::GetAsset<Texture2D>(assetHandle);
shouldUpdate = true;
}
}
ImGui::EndDragDropTarget();
}
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
@ -464,16 +403,12 @@ namespace Prism
{
normalMap = Texture2D::Create(filename);
materialInstance->Set("u_NormalTexture", normalMap);
shouldUpdate = true;
}
}
}
ImGui::SameLine();
if (ImGui::Checkbox("Use##NormalMap", &useNormalMap))
{
materialInstance->Set<float>("u_NormalTexToggle", useNormalMap ? 1.0f : 0.0f);
shouldUpdate = true;
}
}
}
{
@ -487,21 +422,6 @@ namespace Prism
Ref<Texture2D> metalnessMap = materialInstance->TryGetResource<Texture2D>("u_MetalnessTexture");
ImGui::Image(metalnessMap ? (ImTextureRef)metalnessMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64));
if (ImGui::BeginDragDropTarget())
{
if (const auto data = ImGui::AcceptDragDropPayload("asset_payload"))
{
AssetHandle assetHandle = *(AssetHandle*)data->Data;
if (AssetsManager::IsAssetType(assetHandle, AssetType::Texture))
{
metalnessMap = AssetsManager::GetAsset<Texture2D>(assetHandle);
shouldUpdate = true;
}
}
ImGui::EndDragDropTarget();
}
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
@ -514,18 +434,20 @@ namespace Prism
ImGui::Image((ImTextureRef)metalnessMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty())
{
metalnessMap = Texture2D::Create(filename);
materialInstance->Set("u_MetalnessTexture", metalnessMap);
}
}
}
ImGui::SameLine();
if (ImGui::Checkbox("Use##MetalnessMap", &useMetalnessMap))
{
materialInstance->Set<float>("u_MetalnessTexToggle", useMetalnessMap ? 1.0f : 0.0f);
shouldUpdate = true;
}
ImGui::SameLine();
if (ImGui::SliderFloat("Value##MetalnessInput", &metalnessValue, 0.0f, 1.0f))
{
shouldUpdate = true;
}
ImGui::SliderFloat("Value##MetalnessInput", &metalnessValue, 0.0f, 1.0f);
}
}
{
@ -538,20 +460,6 @@ namespace Prism
Ref<Texture2D> roughnessMap = materialInstance->TryGetResource<Texture2D>("u_RoughnessTexture");
ImGui::Image(roughnessMap ? (ImTextureRef)roughnessMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64));
if (ImGui::BeginDragDropTarget())
{
if (const auto data = ImGui::AcceptDragDropPayload("asset_payload"))
{
AssetHandle assetHandle = *(AssetHandle*)data->Data;
if (AssetsManager::IsAssetType(assetHandle, AssetType::Texture))
{
roughnessMap = AssetsManager::GetAsset<Texture2D>(assetHandle);
shouldUpdate = true;
}
}
ImGui::EndDragDropTarget();
}
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
@ -564,71 +472,22 @@ namespace Prism
ImGui::Image((ImTextureRef)roughnessMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
if (ImGui::IsItemClicked())
{
if (std::string filename = FileSystem::OpenFileSelector("*.png;*.tga;*.jpg;*.jpeg"); !filename.empty())
{
roughnessMap = Texture2D::Create(filename);
materialInstance->Set("u_RoughnessTexture", roughnessMap);
}
}
}
ImGui::SameLine();
if (ImGui::Checkbox("Use##RoughnessMap", &useRoughnessMap))
{
materialInstance->Set<float>("u_RoughnessTexToggle", useRoughnessMap ? 1.0f : 0.0f);
shouldUpdate = true;
}
ImGui::SameLine();
if (ImGui::SliderFloat("Value##RoughnessInput", &roughnessValue, 0.0f, 1.0f))
{
shouldUpdate = true;
}
ImGui::SliderFloat("Value##RoughnessInput", &roughnessValue, 0.0f, 1.0f);
}
}
{
// AO
if (ImGui::CollapsingHeader("AO", nullptr, ImGuiTreeNodeFlags_DefaultOpen))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10, 10));
float& aoValue = materialInstance->Get<float>("u_AO");
bool useAOMap = materialInstance->Get<float>("u_AOTexToggle");
Ref<Texture2D> aoMap = materialInstance->TryGetResource<Texture2D>("u_AOTexture");
ImGui::Image(aoMap ? (ImTextureRef)aoMap->GetRendererID() : (ImTextureRef)m_CheckerboardTex->GetRendererID(), ImVec2(64, 64));
if (ImGui::BeginDragDropTarget())
{
if (const auto data = ImGui::AcceptDragDropPayload("asset_payload"))
{
AssetHandle assetHandle = *(AssetHandle*)data->Data;
if (AssetsManager::IsAssetType(assetHandle, AssetType::Texture))
{
aoMap = AssetsManager::GetAsset<Texture2D>(assetHandle);
shouldUpdate = true;
}
}
ImGui::EndDragDropTarget();
}
ImGui::PopStyleVar();
if (ImGui::IsItemHovered())
{
if (aoMap)
{
ImGui::BeginTooltip();
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(aoMap->GetPath().c_str());
ImGui::PopTextWrapPos();
ImGui::Image((ImTextureRef)aoMap->GetRendererID(), ImVec2(384, 384));
ImGui::EndTooltip();
}
}
ImGui::SameLine();
if (ImGui::Checkbox("Use##AOMap", &useAOMap))
{
materialInstance->Set<float>("u_AOTexToggle", useAOMap ? 1.0f : 0.0f);
shouldUpdate = true;
}
ImGui::SameLine();
if (ImGui::SliderFloat("Value##AOInput", &aoValue, 0.0f, 1.0f))
{
shouldUpdate = true;
}
}
}
if (shouldUpdate) meshComponent.UpdateMaterials(selectedMaterialIndex);
}
}
}
@ -646,11 +505,7 @@ namespace Prism
m_ObjectsPanel->OnImGuiRender();
m_SceneHierarchyPanel->OnImGuiRender();
// m_EditorCamera.OnImGuiRender();
m_ConsolePanel->OnImGuiRender();
// GamePad
DrawGamepadDebugPanel(m_ShowGamePadSettings);
m_EditorCamera.OnImGuiRender();
// Editor Panel ------------------------------------------------------------------------------
ImGui::Begin("Environment");
@ -662,20 +517,6 @@ namespace Prism
UI::PropertySlider("Exposure", m_EditorCamera.GetExposure(), 0.0f, 5.0f);
ImGui::Separator();
float near = m_EditorCamera.GetNear();
float far = m_EditorCamera.GetFar();
if (UI::Property("Near", near))
{
m_EditorCamera.SetNearFar(near, far);
}
if (UI::Property("Far", far))
{
m_EditorCamera.SetNearFar(near, far);
}
ImGui::Separator();
float physics2DGravity = m_EditorScene->GetPhysics2DGravity();
if (UI::Property("2D World Gravity", physics2DGravity, -100.0f, 100.0f))
@ -758,6 +599,7 @@ namespace Prism
{
SceneRenderer::SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_EditorScene->SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
m_EditorCamera.SetProjectionMatrix(glm::perspectiveFov(glm::radians(45.0f), viewportSize.x, viewportSize.y, 0.1f, 10000.0f));
m_EditorCamera.SetViewportSize((uint32_t)viewportSize.x, (uint32_t)viewportSize.y);
ImGui::Image((ImTextureRef)SceneRenderer::GetFinalColorBufferRendererID(), viewportSize, { 0, 1 }, { 1, 0 });
@ -812,7 +654,7 @@ namespace Prism
m_ViewportBounds[1] = { maxBound.x, maxBound.y };
// ImGuizmo
if ((m_ViewportPanelFocused || m_ViewportPanelHovered) && m_GizmoType != -1 && !m_SelectionContext.empty() && m_SceneState == SceneState::Edit)
if (m_GizmoType != -1 && !m_SelectionContext.empty() && m_SceneState == SceneState::Edit)
{
auto& selection = m_SelectionContext[0];
@ -1014,19 +856,15 @@ namespace Prism
if (ImViewGuizmo::Rotate(cameraPos, cameraRot, glm::vec3(0.0f), gizmoCenter))
{
if (m_ViewportPanelFocused || m_ViewportPanelHovered)
{
glm::vec3 forward = cameraRot * glm::vec3(0.0f, 0.0f, -1.0f);
float newPitch = glm::asin(glm::clamp(-forward.y, -1.0f, 1.0f));
float newYaw = -glm::atan(forward.x, -forward.z);
float distance = glm::length(cameraPos - focusPosition);
m_EditorCamera.SetDistance(distance);
m_EditorCamera.SetFocusPosition(focusPosition);
m_EditorCamera.SetYawPitch(newYaw, newPitch);
}
glm::vec3 forward = cameraRot * glm::vec3(0.0f, 0.0f, -1.0f);
float newPitch = glm::asin(glm::clamp(-forward.y, -1.0f, 1.0f));
float newYaw = -glm::atan(forward.x, -forward.z);
float distance = glm::length(cameraPos - focusPosition);
m_EditorCamera.SetDistance(distance);
m_EditorCamera.SetFocusPosition(focusPosition);
m_EditorCamera.SetYawPitch(newYaw, newPitch);
}
}
@ -1091,20 +929,20 @@ namespace Prism
break;
}
}
}
switch (e.GetKeyCode())
switch (e.GetKeyCode())
{
case KeyCode::DELETE:
if (m_SelectionContext.size())
{
case KeyCode::DELETE:
if (m_SelectionContext.size())
{
const Entity selectedEntity = m_SelectionContext[0].Entity;
m_EditorScene->DestroyEntity(selectedEntity);
m_SelectionContext.clear();
m_EditorScene->SetSelectedEntity({});
m_SceneHierarchyPanel->SetSelected({});
}
break;
const Entity selectedEntity = m_SelectionContext[0].Entity;
m_EditorScene->DestroyEntity(selectedEntity);
m_SelectionContext.clear();
m_EditorScene->SetSelectedEntity({});
m_SceneHierarchyPanel->SetSelected({});
}
break;
}
if (Input::IsKeyPressed(KeyCode::LEFT_CONTROL))
@ -1182,6 +1020,7 @@ namespace Prism
continue;
auto& submeshes = mesh->GetSubmeshes();
float lastT = std::numeric_limits<float>::max();
for (uint32_t i = 0; i < submeshes.size(); i++)
{
auto& submesh = submeshes[i];
@ -1215,6 +1054,17 @@ namespace Prism
});
if (!m_SelectionContext.empty())
{
if (auto alreadySelectedEntity = m_SceneHierarchyPanel->GetSelected();
m_SelectionContext.size() != 1 && alreadySelectedEntity)
{
const auto alreadySelectedEntityID = alreadySelectedEntity.GetUUID();
for (const auto& selectedEntity : m_SelectionContext)
{
if (alreadySelectedEntityID == selectedEntity.Entity.GetUUID()) continue;
return false;
}
}
OnSelected(m_SelectionContext[0]);
}
}
@ -1255,249 +1105,6 @@ namespace Prism
Application::Get().GetWindow().SetTitle(title);
}
void EditorLayer::DrawGamepadDebugPanel(bool& p_open)
{
if (!p_open)
return;
ImGui::Begin("Gamepad Debug", &p_open);
ImDrawList* drawList = ImGui::GetWindowDrawList();
bool anyConnected = false;
for (int jid = 0; jid <= 15; ++jid)
{
if (!Input::IsGamepadConnected(jid))
continue;
anyConnected = true;
const char* name = Input::GetGamepadName(jid);
bool isGamepad = Input::IsGamepad(jid);
// 使用 CollapsingHeader 或 TreeNodeEx 均可,这里用 TreeNodeEx 保持与之前类似
if (ImGui::TreeNodeEx((void*)(intptr_t)jid, ImGuiTreeNodeFlags_DefaultOpen,
"Joystick %d: %s%s", jid, name, isGamepad ? " (mapped)" : ""))
{
// ---------- 1. 摇杆与主要按钮行 ----------
const float stickRadius = 40.0f; // 圆形摇杆半径
const ImVec2 stickSize(stickRadius * 2, stickRadius * 2);
const float buttonSize = 30.0f; // 普通按钮ABXY的大小
const float dpadButtonSize = 30.0f; // 十字键按钮稍小
// 左摇杆区域
auto drawStick = [&](const char* label, float x, float y)
{
// 注意GLFW 左 Y 向上为负,右 Y 向上为负,统一取反
y = -y;
ImGui::BeginGroup();
ImGui::Text("%s", label);
const ImVec2 pos = ImGui::GetCursorScreenPos();
const ImVec2 center(pos.x + stickRadius, pos.y + stickRadius);
// 圆形底板
drawList->AddCircleFilled(center, stickRadius, IM_COL32(30,30,30,255));
drawList->AddCircle(center, stickRadius, IM_COL32(100,100,100,255), 0, 2.0f);
// 十字参考线
drawList->AddLine(ImVec2(center.x - stickRadius, center.y), ImVec2(center.x + stickRadius, center.y), IM_COL32(70,70,70,255));
drawList->AddLine(ImVec2(center.x, center.y - stickRadius), ImVec2(center.x, center.y + stickRadius), IM_COL32(70,70,70,255));
// 摇杆位置点
const float len = sqrtf(x*x + y*y);
float clampX = x, clampY = y;
if (len > 1.0f) { clampX /= len; clampY /= len; }
const float dotX = center.x + clampX * stickRadius;
const float dotY = center.y - clampY * stickRadius; // 屏幕Y向下故用减
drawList->AddCircleFilled(ImVec2(dotX, dotY), 6.0f, IM_COL32(255,80,80,255));
if (len > 0.99f)
drawList->AddCircle(center, stickRadius, IM_COL32(255,255,0,150), 0, 3.0f);
ImGui::Dummy(stickSize);
ImGui::EndGroup();
};
// 十字键(上下左右)
auto drawDPad = [&]()
{
ImGui::BeginGroup();
ImGui::Text("DPad");
ImVec2 base = ImGui::GetCursorScreenPos();
// 上
bool up = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::DPAD_UP);
ImGui::SetCursorScreenPos(ImVec2(base.x + dpadButtonSize, base.y));
ImGui::PushStyleColor(ImGuiCol_Button, up ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("U", ImVec2(dpadButtonSize, dpadButtonSize));
ImGui::PopStyleColor();
// 左
bool left = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::DPAD_LEFT);
ImGui::SetCursorScreenPos(ImVec2(base.x, base.y + dpadButtonSize));
ImGui::PushStyleColor(ImGuiCol_Button, left ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("L", ImVec2(dpadButtonSize, dpadButtonSize));
ImGui::PopStyleColor();
// 右
bool right = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::DPAD_RIGHT);
ImGui::SetCursorScreenPos(ImVec2(base.x + 2*dpadButtonSize, base.y + dpadButtonSize));
ImGui::PushStyleColor(ImGuiCol_Button, right ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("R", ImVec2(dpadButtonSize, dpadButtonSize));
ImGui::PopStyleColor();
// 下
bool down = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::DPAD_DOWN);
ImGui::SetCursorScreenPos(ImVec2(base.x + dpadButtonSize, base.y + 2*dpadButtonSize));
ImGui::PushStyleColor(ImGuiCol_Button, down ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("D", ImVec2(dpadButtonSize, dpadButtonSize));
ImGui::PopStyleColor();
// 让 Group 大小包含整个十字区域
ImGui::SetCursorScreenPos(ImVec2(base.x, base.y + 3*dpadButtonSize));
ImGui::Dummy(ImVec2(3*dpadButtonSize, 0));
ImGui::EndGroup();
};
// ABXY 按钮组按手柄实际位置排列X左 Y上 B右 A下
auto drawABXY = [&]()
{
ImGui::BeginGroup();
ImGui::Text("Buttons");
ImVec2 base = ImGui::GetCursorScreenPos();
// Y 上
bool y = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::Y);
ImGui::SetCursorScreenPos(ImVec2(base.x + buttonSize, base.y));
ImGui::PushStyleColor(ImGuiCol_Button, y ? ImVec4(1.0f,0.8f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("Y", ImVec2(buttonSize, buttonSize));
ImGui::PopStyleColor();
// X 左
bool x = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::X);
ImGui::SetCursorScreenPos(ImVec2(base.x, base.y + buttonSize));
ImGui::PushStyleColor(ImGuiCol_Button, x ? ImVec4(0.0f,0.7f,1.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("X", ImVec2(buttonSize, buttonSize));
ImGui::PopStyleColor();
// B 右
bool b = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::B);
ImGui::SetCursorScreenPos(ImVec2(base.x + 2*buttonSize, base.y + buttonSize));
ImGui::PushStyleColor(ImGuiCol_Button, b ? ImVec4(1.0f,0.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("B", ImVec2(buttonSize, buttonSize));
ImGui::PopStyleColor();
// A 下
bool a = Prism::Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::A);
ImGui::SetCursorScreenPos(ImVec2(base.x + buttonSize, base.y + 2*buttonSize));
ImGui::PushStyleColor(ImGuiCol_Button, a ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("A", ImVec2(buttonSize, buttonSize));
ImGui::PopStyleColor();
ImGui::SetCursorScreenPos(ImVec2(base.x, base.y + 3*buttonSize));
ImGui::Dummy(ImVec2(3*buttonSize, 0));
ImGui::EndGroup();
};
// 右摇杆区域
auto drawRightStick = [&](float x, float y)
{
y = -y;
ImGui::BeginGroup();
ImGui::Text("R Stick");
ImVec2 pos = ImGui::GetCursorScreenPos();
ImVec2 center(pos.x + stickRadius, pos.y + stickRadius);
drawList->AddCircleFilled(center, stickRadius, IM_COL32(30,30,30,255));
drawList->AddCircle(center, stickRadius, IM_COL32(100,100,100,255), 0, 2.0f);
drawList->AddLine(ImVec2(center.x - stickRadius, center.y), ImVec2(center.x + stickRadius, center.y), IM_COL32(70,70,70,255));
drawList->AddLine(ImVec2(center.x, center.y - stickRadius), ImVec2(center.x, center.y + stickRadius), IM_COL32(70,70,70,255));
float len = sqrtf(x*x + y*y);
float clampX = x, clampY = y;
if (len > 1.0f) { clampX /= len; clampY /= len; }
float dotX = center.x + clampX * stickRadius;
float dotY = center.y - clampY * stickRadius;
drawList->AddCircleFilled(ImVec2(dotX, dotY), 6.0f, IM_COL32(255,80,80,255));
if (len > 0.99f)
drawList->AddCircle(center, stickRadius, IM_COL32(255,255,0,150), 0, 3.0f);
ImGui::Dummy(stickSize);
ImGui::EndGroup();
};
// 获取摇杆当前值
const float lx = Input::GetGamepadAxis(jid, GamepadAxis::LEFT_X);
const float ly = Input::GetGamepadAxis(jid, GamepadAxis::LEFT_Y);
const float rx = Input::GetGamepadAxis(jid, GamepadAxis::RIGHT_X);
const float ry = Input::GetGamepadAxis(jid, GamepadAxis::RIGHT_Y);
// ----- 第一行:左摇杆 | 十字键 | ABXY | 右摇杆 -----
ImGui::BeginGroup();
// 缩进稍微调整
ImGui::Columns(4, "MainControls", false);
ImGui::SetColumnWidth(0, stickSize.x + 10);
ImGui::SetColumnWidth(1, 3*dpadButtonSize + 10);
ImGui::SetColumnWidth(2, 3*buttonSize + 10);
ImGui::SetColumnWidth(3, stickSize.x + 10);
drawStick("L Stick", lx, ly);
ImGui::NextColumn();
drawDPad();
ImGui::NextColumn();
drawABXY();
ImGui::NextColumn();
drawRightStick(rx, ry);
ImGui::Columns(1);
ImGui::EndGroup();
ImGui::Spacing();
// ----- 第二行:肩键 LB/RB 和扳机轴 -----
ImGui::Text("Bumpers & Triggers");
bool lb = Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::LEFT_BUMPER);
bool rb = Input::IsGamepadButtonPressed(jid, Prism::GamepadButton::RIGHT_BUMPER);
float lt = Input::GetGamepadAxis(jid, Prism::GamepadAxis::LEFT_TRIGGER);
float rt = Input::GetGamepadAxis(jid, Prism::GamepadAxis::RIGHT_TRIGGER);
ImGui::BeginGroup();
ImGui::PushStyleColor(ImGuiCol_Button, lb ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("LB", ImVec2(40, 20));
ImGui::PopStyleColor();
ImGui::SameLine();
ImGui::ProgressBar(lt, ImVec2(100, 20), "");
ImGui::SameLine();
ImGui::Text("LT %.2f", lt);
ImGui::SameLine(0, 30);
ImGui::ProgressBar(rt, ImVec2(100, 20), "");
ImGui::SameLine();
ImGui::Text("RT %.2f", rt);
ImGui::SameLine();
ImGui::PushStyleColor(ImGuiCol_Button, rb ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button("RB", ImVec2(40, 20));
ImGui::PopStyleColor();
ImGui::EndGroup();
ImGui::Spacing();
// ----- 第三行:功能键 Back, Guide, Start, LThumb, RThumb -----
ImGui::Text("Function Keys");
auto drawFuncBtn = [&](const char* label, Prism::GamepadButton btn)
{
bool pressed = Input::IsGamepadButtonPressed(jid, btn);
ImGui::PushStyleColor(ImGuiCol_Button, pressed ? ImVec4(0.0f,1.0f,0.0f,1.0f) : ImVec4(0.3f,0.3f,0.3f,1.0f));
ImGui::Button(label, ImVec2(60, 25));
ImGui::PopStyleColor();
ImGui::SameLine(0, 4);
};
drawFuncBtn("Back", GamepadButton::BACK);
drawFuncBtn("Guide", GamepadButton::GUIDE);
drawFuncBtn("Start", GamepadButton::START);
ImGui::SameLine(0, 20);
drawFuncBtn("LThumb", GamepadButton::LEFT_THUMB);
drawFuncBtn("RThumb", GamepadButton::RIGHT_THUMB);
ImGui::NewLine();
ImGui::TreePop();
}
}
if (!anyConnected)
ImGui::Text("No gamepads connected.");
ImGui::End();
}
std::pair<float, float> EditorLayer::GetMouseViewportSpace() const
{
auto [mx, my] = ImGui::GetMousePos(); // Input::GetMousePosition();
@ -1557,16 +1164,8 @@ namespace Prism
UpdateWindowTitle("Untitled Scene");
m_SceneFilePath = std::string();
m_EditorCamera = EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f));
m_EditorCamera = EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 100.0f));
m_SelectionContext.clear();
// new Scene with a default sky light
auto newEntity = m_EditorScene->CreateEntity("Sky Light");
auto& slc = newEntity.AddComponent<SkyLightComponent>();
slc.DynamicSky = true;
Ref<TextureCube> preethamEnv = Renderer3D::CreatePreethamSky(slc.TurbidityAzimuthInclination);
slc.SceneEnvironment = Ref<Environment>::Create(preethamEnv, preethamEnv);
}
void EditorLayer::OpenScene()
@ -1633,7 +1232,7 @@ namespace Prism
m_SceneState = SceneState::Play;
if (m_ReloadScriptOnPlay)
ScriptEngine::ReloadAssembly("assets/Scripts/Assembly-CSharp.dll");
ScriptEngine::ReloadAssembly("assets/scripts/ExampleApp.dll");
m_RuntimeScene = Ref<Scene>::Create();
m_EditorScene->CopyTo(m_RuntimeScene);

View File

@ -5,7 +5,6 @@
#ifndef EDITORLAYER_H
#define EDITORLAYER_H
#include "Panels/ConsolePanel.h"
#include "Prism.h"
#include "Prism/Editor/ContentBrowserPanel.h"
#include "Prism/Editor/ObjectsPanel.h"
@ -33,10 +32,6 @@ namespace Prism
void SelectEntity(Entity entity);
void UpdateWindowTitle(const std::string& sceneName);
// GamePad Utils
void DrawGamepadDebugPanel(bool& p_open);
private:
std::pair<float, float> GetMouseViewportSpace() const;
std::pair<glm::vec3, glm::vec3> CastRay(float mx, float my);
@ -149,16 +144,12 @@ namespace Prism
bool m_ViewportPanelHovered = false;
bool m_ViewportPanelFocused = false;
bool m_ShowPhysicsSettings = false;
bool m_ShowGamePadSettings = false;
enum class SceneState
{
Edit = 0, Play = 1, Pause = 2
};
SceneState m_SceneState = SceneState::Edit;
private:
Scope<ConsolePanel> m_ConsolePanel;
};
}

View File

@ -1,66 +0,0 @@
//
// Created by Atdunbg on 2026/3/26.
//
#include "ConsolePanel.h"
namespace Prism
{
ConsolePanel::ConsolePanel()
{
// 预留一些空间
m_Messages.reserve(1000);
}
void ConsolePanel::OnImGuiRender()
{
ImGui::Begin("Console");
// 工具栏
if (ImGui::Button("Clear"))
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Messages.clear();
}
ImGui::SameLine();
ImGui::Checkbox("Auto-scroll", &m_AutoScroll);
ImGui::SameLine();
m_Filter.Draw("Filter", 200);
// 消息列表区域
ImGui::BeginChild("ScrollingRegion", ImVec2(0, 0), ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);
{
std::lock_guard<std::mutex> lock(m_Mutex);
for (const auto& [msg, color] : m_Messages)
{
if (m_Filter.PassFilter(msg.c_str()))
{
ImGui::PushStyleColor(ImGuiCol_Text, color);
ImGui::TextUnformatted(msg.c_str());
ImGui::PopStyleColor();
}
}
if (m_ScrollToBottom)
{
ImGui::SetScrollHereY(1.0f);
m_ScrollToBottom = false;
}
}
ImGui::EndChild();
ImGui::End();
}
void ConsolePanel::AddMessage(const std::string& message, ImVec4 color)
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_Messages.emplace_back(message, color);
while (m_Messages.size() > 5000)
m_Messages.erase(m_Messages.begin());
if (m_AutoScroll && !m_ScrollToBottom)
m_ScrollToBottom = true;
}
}

View File

@ -1,31 +0,0 @@
//
// Created by Atdunbg on 2026/3/26.
//
#ifndef PRISM_CONSOLEPANEL_H
#define PRISM_CONSOLEPANEL_H
#include <mutex>
#include <vector>
#include "imgui.h"
namespace Prism
{
class ConsolePanel
{
public:
ConsolePanel();
void OnImGuiRender();
void AddMessage(const std::string& message, ImVec4 color = ImVec4(1,1,1,1));
private:
std::vector<std::pair<std::string, ImVec4>> m_Messages;
ImGuiTextFilter m_Filter;
bool m_AutoScroll = true;
bool m_ScrollToBottom = false;
std::mutex m_Mutex;
};
}
#endif //PRISM_CONSOLEPANEL_H

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

View File

@ -1,389 +0,0 @@
Scene: Scene Name
Environment:
AssetHandle: 10745193190519058183
Entities:
- Entity: 4944419254382500800
Parent: 0
Children:
[]
TagComponent:
Tag: Directional Light
TransformComponent:
Position: [0, 0, 0]
Rotation: [-2.2312348, 0, 0]
Scale: [1, 1, 1]
DirectionalLightComponent:
Radiance: [1, 1, 1]
CastShadows: true
SoftShadows: true
LightSize: 0.5
- Entity: 5099152432245948441
Parent: 0
Children:
[]
TagComponent:
Tag: venice_dawn_1_4k
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
SkyLightComponent:
EnvironmentMap: 10745193190519058183
EnvironmentAssetPath: assets/env/venice_dawn_1_4k.hdr
Intensity: 1
Angle: 0
DynamicSky: false
TurbidityAzimuthInclination: [2, 0, 0]
- Entity: 10732070446010033158
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, -2.6466873, 0]
Rotation: [0, 0, 0]
Scale: [100, 1, 100]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 0
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
- Entity: 9267298328378270409
Parent: 0
Children:
[]
TagComponent:
Tag: Player
TransformComponent:
Position: [0, 0.70693016, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: FPSExample.FPSPlayer
StoredFields:
- Name: m_Radius
Type: 1
Data: 0
- Name: WalkingSpeed
Type: 1
Data: 2
- Name: RunSpeed
Type: 1
Data: 5
- Name: JumpForce
Type: 1
Data: 1
- Name: TorqueStrength
Type: 1
Data: 0
- Name: MouseSensitivity
Type: 1
Data: 10
- Name: CameraForwardOffset
Type: 1
Data: -2
- Name: CameraYOffset
Type: 1
Data: 2
MeshComponent:
AssetID: 8763440120556133680
AssetPath: assets/meshes/Default/Capsule.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: true
LockRotationY: false
LockRotationZ: true
MeshColliderComponent:
IsConvex: true
IsTrigger: false
OverrideMesh: false
Material: 0
MaterialPath: ""
- Entity: 8114736924719261351
Parent: 0
Children:
[]
TagComponent:
Tag: Camera
TransformComponent:
Position: [0, 0.8097433, 4.573171]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
CameraComponent:
Camera:
ProjectionType: 0
PerspectiveFOV: 45
PerspectiveNear: 0.01
PerspectiveFar: 10000
OrthographicSize: 10
OrthographicNear: -1
OrthographicFar: 1
Primary: true
- Entity: 4208267561919679628
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [-2.6417403, 1.4724115, -4.8956265]
Rotation: [-0.4034239, 0, 0]
Scale: [1, 0.99999994, 0.99999994]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: true
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
- Entity: 3328246672296261054
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [1.736814, 1.4724115, -0.88378817]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
- Entity: 12935252585493481950
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [-1.5106764, 6.237644, -4.2181306]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
- Entity: 8234256119181302872
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [1.736814, 1.4724115, -7.9285727]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
- Entity: 5834225236589765516
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [-2.6417403, 1.4724115, -7.9285727]
Rotation: [0.52199936, 0, 0]
Scale: [1, 1.0000001, 1.0000001]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
- Entity: 8293051279669100759
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [1.736814, 1.4724115, -4.2181306]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 14957733959243172548
AssetPath: assets/meshes/Default/Cube.fbx
Materials:
Slot 0:
AssetHandle: 0
AssetPath: ""
RigidBodyComponent:
BodyType: 1
Mass: 1
LinearDrag: 0
AngularDrag: 0.05
DisableGravity: false
IsKinematic: false
Layer: 0
Constraints:
LockPositionX: false
LockPositionY: false
LockPositionZ: false
LockRotationX: false
LockRotationY: false
LockRotationZ: false
BoxColliderComponent:
Offset: [0, 0, 0]
Size: [2, 2, 2]
IsTrigger: false
Material: 0
MaterialPath: ""
PhysicsLayers:
[]

View File

@ -1,50 +1,11 @@
Scene: Scene Name
Environment:
AssetHandle: 6095149963749185931
AssetHandle: 10549690553241162923
Light:
Direction: [-0.314, -0.941, -0.209]
Radiance: [0, 0, 0]
Multiplier: 1
Entities:
- Entity: 6421668200759325475
Parent: 0
Children:
[]
TagComponent:
Tag: M1911Materials
TransformComponent:
Position: [0, 3.159583, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 7219694555758922702
AssetPath: assets/models/m1911/M1911Materials.fbx
- Entity: 16992665426857995732
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [50, 1, 50]
MeshComponent:
AssetID: 18328012085543462741
AssetPath: assets/meshes/Default/Cube.fbx
- Entity: 18182275256052989728
Parent: 0
Children:
[]
TagComponent:
Tag: Sky Light
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
SkyLightComponent:
EnvironmentMap: 6095149963749185931
EnvironmentAssetPath: ""
Intensity: 1
Angle: 0
DynamicSky: true
TurbidityAzimuthInclination: [2, 0.15, 0.71]
- Entity: 17803125207910630398
Parent: 0
Children:
@ -59,6 +20,30 @@ Entities:
Radiance: [1, 1, 1]
CastShadows: true
SoftShadows: true
LightSize: 0.9
LightSize: 0.5
- Entity: 4315886439647742331
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 2.048974, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 3580169978473467053
- Entity: 16992665426857995732
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [50, 1, 50]
MeshComponent:
AssetID: 3580169978473467053
PhysicsLayers:
[]

View File

@ -1,21 +1,12 @@
Scene: Scene Name
Environment:
AssetHandle: 5211537204242875091
AssetHandle: 17073147362577408906
Light:
Direction: [-0.314, -0.941, -0.209]
Radiance: [0, 0, 0]
Multiplier: 1
Entities:
- Entity: 15706224176559717512
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 18328012085543462741
AssetPath: assets/meshes/Default/Cube.fbx
- Entity: 8041206185299282567
- Entity: 3696833073589069488
Parent: 0
Children:
[]
@ -26,11 +17,8 @@ Entities:
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
SkyLightComponent:
EnvironmentMap: 5211537204242875091
EnvironmentAssetPath: assets/env/venice_dawn_1_4k.hdr
EnvironmentMap: 17073147362577408906
Intensity: 1
Angle: 0
DynamicSky: false
TurbidityAzimuthInclination: [2, 0, 0]
PhysicsLayers:
[]

View File

@ -24,16 +24,16 @@ uniform sampler2D u_Texture;
uniform bool u_Horizontal; // 未使用,可保留或移除
uniform bool u_FirstPass; // 是否进行阈值处理
uniform float u_Threshold; // 亮度阈值
uniform int u_Quality;
uniform float u_Directions; // 模糊方向数
uniform float u_Size; // 模糊半径
void main()
{
float Pi = 6.28318530718; // 2*PI
float Directions = 32.0; // 模糊方向数
float Quality = 6.0; // 每个方向上的采样质量(采样次数)
float Size = 16.0; // 模糊半径
vec2 Radius = Size / textureSize(u_Texture, 0);
vec2 Radius = u_Size / textureSize(u_Texture, 0);
// 中心像素采样
vec3 centerColor = texture(u_Texture, v_TexCoord).rgb;
@ -50,9 +50,9 @@ void main()
float totalSamples = 1.0; // 有效采样计数(中心像素已计入)
// 周围像素采样
for (float d = 0.0; d < Pi; d += Pi / Directions)
for (float d = 0.0; d < Pi; d += Pi / u_Directions)
{
for (float i = 1.0 / Quality; i <= 1.0; i += 1.0 / Quality)
for (float i = 1.0 / u_Quality; i <= 1.0; i += 1.0 / u_Quality)
{
vec2 offset = vec2(cos(d), sin(d)) * Radius * i;
vec3 sampleColor = texture(u_Texture, v_TexCoord + offset).rgb;

View File

@ -107,16 +107,16 @@ void main(void)
if(gl_GlobalInvocationID.x >= outputSize.x || gl_GlobalInvocationID.y >= outputSize.y) {
return;
}
// Solid angle associated with a single cubemap texel at zero mipmap level.
// This will come in handy for importance sampling below.
vec2 inputSize = vec2(textureSize(inputTexture, 0));
float wt = 4.0 * PI / (6 * inputSize.x * inputSize.y);
// Approximation: Assume zero viewing angle (isotropic reflections).
vec3 N = GetCubeMapTexCoord();
vec3 Lo = N;
vec3 S, T;
computeBasisVectors(N, S, T);

View File

@ -0,0 +1,348 @@
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
v_TexCoord = a_TexCoord;
gl_Position = vec4(a_Position.xy, 0.0, 1.0);
}
#type fragment
#version 430 core
// ==================== 输入 ====================
in vec2 v_TexCoord;
// G-buffer 纹理
uniform sampler2D u_AlbedoMetallic; // RGB: albedo, A: metallic
uniform sampler2D u_NormalRoughness; // RGB: normal (encoded), A: roughness
uniform sampler2D u_EmissiveAO; // RGB: emissive, A: AO
uniform sampler2D u_Depth; // depth
// 相机参数
uniform mat4 u_InvViewProj; // 逆视图投影矩阵,用于重建世界坐标
uniform vec3 u_CameraPosition;
// 光源结构体与你的PBR着色器一致
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
float Intensity;
bool CastShadows;
};
struct PointLight {
vec3 Position;
vec3 Radiance;
float Intensity;
float Range;
bool CastShadows;
};
struct SpotLight {
vec3 Position;
vec3 Direction;
vec3 Radiance;
float Intensity;
float Range;
float InnerConeCos;
float OuterConeCos;
bool CastShadows;
};
uniform DirectionalLight u_DirectionalLights; // 仅一个方向光
uniform int u_PointLightCount;
uniform PointLight u_PointLights[16]; // 假设最多16个点光源
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights[16]; // 最多16个聚光源
// IBL 相关
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
uniform sampler2D u_BRDFLUTTexture;
uniform float u_IBLContribution;
uniform float u_EnvMapRotation;
// 阴影相关
uniform sampler2D u_ShadowMap;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform int u_ShadowEnabled;
uniform float u_ShadowIntensity; // 阴影强度0-1
uniform mat4 u_LightSpaceMatrix; // 方向光光源空间矩阵
// 天空盒(可选)
uniform samplerCube u_Skybox; // 如果深度为1.0则采样天空盒
uniform float u_SkyIntensity;
uniform float u_SkyTextureLod;
// 输出
layout(location = 0) out vec4 o_Color;
// ==================== 常量 ====================
const float PI = 3.14159265359;
const float Epsilon = 0.00001;
const vec3 Fdielectric = vec3(0.04);
// ==================== 工具函数 ====================
// 从深度重建世界坐标
vec3 worldPosFromDepth(vec2 uv, float depth) {
vec4 clipPos = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec4 worldPos = u_InvViewProj * clipPos;
return worldPos.xyz / worldPos.w;
}
// 从深度重建世界空间方向(用于天空盒采样)
vec3 worldDirFromUV(vec2 uv) {
// 假设深度为1.0时,得到远平面方向
vec4 clipPos = vec4(uv * 2.0 - 1.0, 1.0, 1.0);
vec4 worldPos = u_InvViewProj * clipPos;
return normalize(worldPos.xyz / worldPos.w);
}
// 旋转向量绕Y轴
vec3 RotateVectorAboutY(float angle, vec3 vec) {
angle = radians(angle);
mat3 rotationMatrix = mat3(
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
);
return rotationMatrix * vec;
}
// ==================== PBR 函数(复用你的代码) ====================
float ndfGGX(float cosLh, float roughness) {
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) {
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------- 方向光 ----------
vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, vec3 N, vec3 V, float NdotV, vec3 albedo, float roughness, float metallic) {
vec3 L = normalize(-light.Direction);
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 Lh = normalize(L + V);
float cosLi = max(dot(N, L), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
vec3 F = fresnelSchlick(F0, max(dot(Lh, V), 0.0));
float D = ndfGGX(cosLh, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseBRDF = kd * albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
// ---------- 点光源 ----------
vec3 ComputePointLight(PointLight light, vec3 F0, vec3 N, vec3 V, float NdotV, vec3 albedo, float roughness, float metallic, vec3 worldPos) {
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
vec3 Lh = normalize(L + V);
float cosLi = max(dot(N, L), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
vec3 F = fresnelSchlick(F0, max(dot(Lh, V), 0.0));
float D = ndfGGX(cosLh, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseBRDF = kd * albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
// ---------- 聚光源 ----------
vec3 ComputeSpotLight(SpotLight light, vec3 F0, vec3 N, vec3 V, float NdotV, vec3 albedo, float roughness, float metallic, vec3 worldPos) {
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
float cosAngle = dot(-L, normalize(light.Direction));
if (cosAngle < light.OuterConeCos) return vec3(0.0);
float angleFalloff = (cosAngle - light.OuterConeCos) / (light.InnerConeCos - light.OuterConeCos);
angleFalloff = clamp(angleFalloff, 0.0, 1.0);
attenuation *= angleFalloff;
vec3 Lh = normalize(L + V);
float cosLi = max(dot(N, L), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
vec3 F = fresnelSchlick(F0, max(dot(Lh, V), 0.0));
float D = ndfGGX(cosLh, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseBRDF = kd * albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
// ---------- IBL ----------
vec3 IBL(vec3 F0, vec3 N, vec3 V, float NdotV, float roughness, float metallic, vec3 albedo) {
vec3 irradiance = texture(u_EnvIrradianceTex, N).rgb;
vec3 F = fresnelSchlickRoughness(F0, NdotV, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseIBL = albedo * irradiance;
vec3 R = 2.0 * NdotV * N - V; // 反射向量
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, R),
roughness * u_EnvRadianceTexLevels
).rgb;
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(NdotV, 1.0 - roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
// ---------- 阴影 ----------
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) {
if (u_ShadowEnabled == 0) return 0.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
int pcfRange = int(u_ShadowSoftness);
int sampleCount = 0;
for (int x = -pcfRange; x <= pcfRange; ++x) {
for (int y = -pcfRange; y <= pcfRange; ++y) {
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += (currentDepth - bias > pcfDepth) ? 1.0 : 0.0;
sampleCount++;
}
}
shadow /= float(sampleCount);
return shadow * u_ShadowIntensity; // 应用阴影强度
}
// ==================== 主函数 ====================
void main() {
vec2 uv = v_TexCoord;
float depth = texture(u_Depth, uv).r;
if (depth >= 1.0) {
vec3 dir = worldDirFromUV(uv);
vec3 skyColor = textureLod(u_Skybox, dir, u_SkyTextureLod).rgb * u_SkyIntensity;
o_Color = vec4(skyColor, 1.0);
return;
}
vec4 albedoMetal = texture(u_AlbedoMetallic, uv);
vec4 normalRough = texture(u_NormalRoughness, uv);
vec4 emissiveAO = texture(u_EmissiveAO, uv);
vec3 albedo = albedoMetal.rgb;
float metallic = albedoMetal.a;
vec3 normal = normalRough.rgb * 2.0 - 1.0;
float roughness = normalRough.a;
vec3 emissive = emissiveAO.rgb;
float ao = emissiveAO.a;
vec3 worldPos = worldPosFromDepth(uv, depth);
vec3 V = normalize(u_CameraPosition - worldPos);
float NdotV = clamp(dot(normal, V), 0.0, 1.0);
vec3 F0 = mix(Fdielectric, albedo, metallic);
vec3 Lo = vec3(0.0);
// Direction Light
if (u_DirectionalLights.Intensity > 0.0) {
Lo += ComputeDirectionalLight(u_DirectionalLights, F0, normal, V, NdotV, albedo, roughness, metallic);
}
// Point Light
for (int i = 0; i < u_PointLightCount; ++i) {
Lo += ComputePointLight(u_PointLights[i], F0, normal, V, NdotV, albedo, roughness, metallic, worldPos);
}
// Spot light
for (int i = 0; i < u_SpotLightCount; ++i) {
Lo += ComputeSpotLight(u_SpotLights[i], F0, normal, V, NdotV, albedo, roughness, metallic, worldPos);
}
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0 && u_DirectionalLights.CastShadows && u_DirectionalLights.Intensity > 0.0) {
vec4 fragPosLightSpace = u_LightSpaceMatrix * vec4(worldPos, 1.0);
float shadow = calculateShadow(fragPosLightSpace, normal, u_DirectionalLights.Direction);
shadowFactor = 1.0 - shadow;
}
Lo *= shadowFactor;
// 计算 IBL
vec3 ibl = IBL(F0, normal, V, NdotV, roughness, metallic, albedo) * u_IBLContribution;
vec3 finalColor = Lo + ibl + emissive;
o_Color = vec4(finalColor, 1.0);
}

View File

@ -1,4 +1,4 @@
// -----------------------------
// -----------------------------
// -- Based on Hazel PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
@ -28,6 +28,8 @@ uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform;
uniform mat4 u_LightSpaceMatrix;
out VertexOutput
{
vec3 WorldPosition;
@ -37,7 +39,7 @@ out VertexOutput
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
float ViewZ;
vec4 FragPosLightSpace;
} vs_Output;
void main()
@ -56,8 +58,9 @@ void main()
vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * localPosition;
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
vs_Output.ViewZ = vs_Output.ViewPosition.z;
// gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition;
@ -72,10 +75,6 @@ const float Epsilon = 0.00001;
const int LightCount = 1;
const vec3 Fdielectric = vec3(0.04);
layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_MaterialInfo;
layout(location = 2) out vec4 o_BloomColor;
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
@ -111,18 +110,20 @@ in VertexOutput
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
float ViewZ;
vec4 FragPosLightSpace;
} vs_Input;
layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_BloomColor;
uniform DirectionalLight u_DirectionalLights;
uniform vec3 u_CameraPosition;
uniform int u_PointLightCount;
uniform PointLight u_PointLights[8];
uniform PointLight u_PointLights;
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights[8];
uniform SpotLight u_SpotLights;
// PBR
@ -130,7 +131,6 @@ uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
uniform sampler2D u_AOTexture;
// environment
uniform samplerCube u_EnvRadianceTex;
@ -144,36 +144,33 @@ uniform float u_BloomThreshold;
uniform float u_EnvMapRotation;
// baseColor
uniform vec4 u_AlbedoColor;
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_AO;
// textureToggle
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
uniform float u_AOTexToggle;
// shadow
const int CSM_CASCADE_COUNT = 4;
uniform sampler2D u_ShadowMap[4];
uniform mat4 u_LightSpaceMatrix0;
uniform mat4 u_LightSpaceMatrix1;
uniform mat4 u_LightSpaceMatrix2;
uniform mat4 u_LightSpaceMatrix3;
uniform vec4 u_CascadeSplits;
uniform float u_ShadowFar;
uniform float u_ShadowNear;
uniform sampler2D u_ShadowMap;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform float u_ShadowIntensity;
uniform int u_ShadowEnabled;
// Emissive
uniform sampler2D u_EmissiveTexture;
uniform float u_EmissiveTexToggle;
uniform vec3 u_EmissiveColor;
uniform float u_EmissiveIntensity;
struct PBRParameters
{
vec4 Albedo;
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
@ -235,7 +232,7 @@ vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, PBRParameters para
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo.rgb;
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
@ -267,7 +264,7 @@ vec3 ComputePointLight(PointLight light, vec3 F0, PBRParameters params, vec3 wor
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo.rgb;
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
@ -304,7 +301,7 @@ vec3 ComputeSpotLight(SpotLight light, vec3 F0, PBRParameters params, vec3 world
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo.rgb;
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
@ -329,7 +326,7 @@ vec3 Lighting(vec3 F0)
float G = GeometrySmith(m_Params.Normal, m_Params.View, Li, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo.rgb;
vec3 diffuseBRDF = kd * m_Params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
@ -355,7 +352,7 @@ vec3 IBL(vec3 F0, vec3 Lr)
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo.rgb * irradiance;
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
vec3 specularIrradiance = textureLod(
@ -371,125 +368,84 @@ vec3 IBL(vec3 F0, vec3 Lr)
}
// shadow
int selectCascade(float viewZ)
{
float depth = -viewZ;
float linearDepth = (depth - u_ShadowNear) / (u_ShadowFar - u_ShadowNear);
if (linearDepth < u_CascadeSplits.x) return 0;
if (linearDepth < u_CascadeSplits.y) return 1;
if (linearDepth < u_CascadeSplits.z) return 2;
return 3;
}
mat4 getLightSpaceMatrix(int cascade)
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
{
if (cascade == 0) return u_LightSpaceMatrix0;
else if (cascade == 1) return u_LightSpaceMatrix1;
else if (cascade == 2) return u_LightSpaceMatrix2;
else return u_LightSpaceMatrix3;
}
// Perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
float getCascadeSplit(int cascade)
{
if (cascade == 0) return u_CascadeSplits.x;
else if (cascade == 1) return u_CascadeSplits.y;
else if (cascade == 2) return u_CascadeSplits.z;
else return u_CascadeSplits.w;
}
// Transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
float sampleShadow(int cascade, vec2 uv, float compareDepth, float bias)
{
float depth;
if (cascade == 0) depth = texture(u_ShadowMap[0], uv).r;
else if (cascade == 1) depth = texture(u_ShadowMap[1], uv).r;
else if (cascade == 2) depth = texture(u_ShadowMap[2], uv).r;
else depth = texture(u_ShadowMap[3], uv).r;
return (compareDepth - bias) > depth ? 1.0 : 0.0;
}
// If outside shadow map bounds, assume no shadow
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
vec2 getTexelSize(int cascade)
{
if (cascade == 0) return 1.0 / vec2(textureSize(u_ShadowMap[0], 0));
else if (cascade == 1) return 1.0 / vec2(textureSize(u_ShadowMap[1], 0));
else if (cascade == 2) return 1.0 / vec2(textureSize(u_ShadowMap[2], 0));
else return 1.0 / vec2(textureSize(u_ShadowMap[3], 0));
}
// Get closest depth value from light's perspective
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float pcfShadow(int cascade, vec3 projCoords, float bias)
{
vec2 texelSize = getTexelSize(cascade);
int pcfRange = clamp(int(u_ShadowSoftness), 0, 3);
// Calculate bias based on surface angle
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
// PCF (Percentage Closer Filtering) for soft shadows
float shadow = 0.0;
int samples = 0;
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
int pcfRange = int(u_ShadowSoftness);
int sampleCount = 0;
for (int x = -pcfRange; x <= pcfRange; ++x)
for(int x = -pcfRange; x <= pcfRange; ++x)
{
for (int y = -pcfRange; y <= pcfRange; ++y)
for(int y = -pcfRange; y <= pcfRange; ++y)
{
vec2 offset = vec2(x, y) * texelSize;
shadow += sampleShadow(cascade, projCoords.xy + offset, projCoords.z, bias);
samples++;
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
sampleCount++;
}
}
return shadow / float(samples);
shadow /= float(sampleCount);
return shadow;
}
float calculateCSMShadow(vec3 worldPos, vec3 normal, vec3 lightDir, float viewZ)
{
int cascade = selectCascade(viewZ);
vec4 fragPosLightSpace = getLightSpaceMatrix(cascade) * vec4(worldPos, 1.0);
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
{
if (u_ShadowEnabled == 0) return 1.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0) return 1.0;
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
float shadow = pcfShadow(cascade, projCoords, bias);
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float cascadeEdge = 0.0;
if (cascade < CSM_CASCADE_COUNT - 1)
{
float depth = -viewZ;
float linearDepth = (depth - u_ShadowNear) / (u_ShadowFar - u_ShadowNear);
float splitDist = getCascadeSplit(cascade);
float prevSplit = (cascade > 0) ? getCascadeSplit(cascade - 1) : 0.0;
float blendStart = splitDist - (splitDist - prevSplit) * 0.1;
if (linearDepth > blendStart)
{
cascadeEdge = (linearDepth - blendStart) / (splitDist - blendStart);
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
int nextCascade = cascade + 1;
vec4 fragPosNext = getLightSpaceMatrix(nextCascade) * vec4(worldPos, 1.0);
vec3 projCoordsNext = fragPosNext.xyz / fragPosNext.w;
projCoordsNext = projCoordsNext * 0.5 + 0.5;
if (!(projCoordsNext.z > 1.0 || projCoordsNext.x < 0.0 || projCoordsNext.x > 1.0 ||
projCoordsNext.y < 0.0 || projCoordsNext.y > 1.0))
{
float nextShadow = pcfShadow(nextCascade, projCoordsNext, bias);
shadow = mix(shadow, nextShadow, cascadeEdge);
}
}
}
return shadow * u_ShadowIntensity;
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
}
void main()
{
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord) : u_AlbedoColor;
float alpha = 1.0;
if (u_AlbedoTexToggle > 0.5) {
vec4 albedoWithAlpha = texture(u_AlbedoTexture, vs_Input.TexCoord);
m_Params.Albedo = albedoWithAlpha.rgb;
alpha = albedoWithAlpha.a;
} else {
m_Params.Albedo = u_AlbedoColor;
alpha = 1.0;
}
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05);
float ao = u_AOTexToggle > 0.5 ? texture(u_AOTexture, vs_Input.TexCoord).r : u_AO;
// normal
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
@ -503,11 +459,11 @@ void main()
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
vec3 F0 = mix(Fdielectric, m_Params.Albedo.rgb, m_Params.Metalness);
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0.5) {
float shadow = calculateCSMShadow(vs_Input.WorldPosition, m_Params.Normal, u_DirectionalLights.Direction, vs_Input.ViewZ);
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
shadowFactor = 1.0 - shadow;
}
@ -515,27 +471,26 @@ void main()
vec3 lightContribution = u_DirectionalLights.Intensity > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
if(u_PointLightCount > 0)
{
for( int i = 0; i < u_PointLightCount; i ++) {
lightContribution += ComputePointLight(u_PointLights[i], F0, m_Params, vs_Input.WorldPosition);
}
}
lightContribution += ComputePointLight(u_PointLights, F0, m_Params, vs_Input.WorldPosition);
if(u_SpotLightCount > 0)
{
for(int i = 0; i < u_SpotLightCount; i ++)
{
lightContribution += ComputeSpotLight(u_SpotLights[i], F0, m_Params, vs_Input.WorldPosition);
}
lightContribution += ComputeSpotLight(u_SpotLights, F0, m_Params, vs_Input.WorldPosition);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
vec3 emissive = u_EmissiveColor;
if (u_EmissiveTexToggle > 0.5) {
emissive = texture(u_EmissiveTexture, vs_Input.TexCoord).rgb;
}
emissive *= u_EmissiveIntensity;
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution * ao;
vec3 finalRGB = lightContribution + iblContribution + emissive;
vec4 finalColor = vec4(finalRGB, alpha);
color = vec4(lightContribution + iblContribution, 1.0);
color = finalColor;
// Bloom
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
o_BloomColor = brightness > u_BloomThreshold ? color : vec4(0.0, 0.0, 0.0, 1.0);
o_MaterialInfo = vec4(m_Params.Metalness, m_Params.Roughness, 0.0, 1.0);
}

View File

@ -1,4 +1,4 @@
// -----------------------------
// -----------------------------
// -- Based on Hazel PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
@ -22,6 +22,8 @@ uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform;
uniform mat4 u_LightSpaceMatrix;
out VertexOutput
{
vec3 WorldPosition;
@ -31,7 +33,7 @@ out VertexOutput
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
float ViewZ;
vec4 FragPosLightSpace;
} vs_Output;
void main()
@ -43,8 +45,9 @@ void main()
vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * vec4(a_Position, 1.0);
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
vs_Output.ViewZ = vs_Output.ViewPosition.z;
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
}
@ -52,16 +55,18 @@ void main()
#type fragment
#version 430 core
layout(location = 0) out vec4 outAlbedoMetal;
layout(location = 1) out vec4 outNormalRoughness;
layout(location = 2) out vec4 outEmissiveAO;
layout(location = 3) out vec4 outColor;
layout(location = 4) out vec4 outBloomColor;
const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
const vec3 Fdielectric = vec3(0.04);
layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_MaterialInfo;
layout(location = 2) out vec4 o_BloomColor;
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
@ -98,16 +103,19 @@ in VertexOutput
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
float ViewZ;
vec4 FragPosLightSpace;
} vs_Input;
uniform bool u_GBufferMode;
uniform DirectionalLight u_DirectionalLights;
uniform int u_PointLightCount;
uniform PointLight u_PointLights[8];
uniform PointLight u_PointLights;
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights[8];
uniform SpotLight u_SpotLights;
uniform vec3 u_CameraPosition;
@ -117,7 +125,6 @@ uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
uniform sampler2D u_AOTexture;
// environment
uniform samplerCube u_EnvRadianceTex;
@ -131,36 +138,32 @@ uniform float u_BloomThreshold;
uniform float u_EnvMapRotation;
// baseColor
uniform vec4 u_AlbedoColor;
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_AO;
// textureToggle
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
uniform float u_AOTexToggle;
// shadow
const int CSM_CASCADE_COUNT = 4;
uniform sampler2D u_ShadowMap[4];
uniform mat4 u_LightSpaceMatrix0;
uniform mat4 u_LightSpaceMatrix1;
uniform mat4 u_LightSpaceMatrix2;
uniform mat4 u_LightSpaceMatrix3;
uniform vec4 u_CascadeSplits;
uniform float u_ShadowFar;
uniform float u_ShadowNear;
uniform sampler2D u_ShadowMap;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform float u_ShadowIntensity;
uniform int u_ShadowEnabled;
// Emissive
uniform sampler2D u_EmissiveTexture;
uniform float u_EmissiveTexToggle;
uniform vec3 u_EmissiveColor;
uniform float u_EmissiveIntensity;
struct PBRParameters
{
vec4 Albedo;
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
@ -222,7 +225,7 @@ vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, PBRParameters para
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo.rgb;
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
@ -254,7 +257,7 @@ vec3 ComputePointLight(PointLight light, vec3 F0, PBRParameters params, vec3 wor
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo.rgb;
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
@ -291,7 +294,7 @@ vec3 ComputeSpotLight(SpotLight light, vec3 F0, PBRParameters params, vec3 world
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo.rgb;
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
@ -314,7 +317,7 @@ vec3 Lighting(vec3 F0)
float G = GeometrySmith(m_Params.Normal, m_Params.View, Li, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo.rgb;
vec3 diffuseBRDF = kd * m_Params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
@ -340,14 +343,14 @@ vec3 IBL(vec3 F0, vec3 Lr)
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo.rgb * irradiance;
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
).rgb;
).rgb;
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
@ -356,174 +359,139 @@ vec3 IBL(vec3 F0, vec3 Lr)
}
// shadow
int selectCascade(float viewZ)
{
float depth = -viewZ;
float linearDepth = (depth - u_ShadowNear) / (u_ShadowFar - u_ShadowNear);
if (linearDepth < u_CascadeSplits.x) return 0;
if (linearDepth < u_CascadeSplits.y) return 1;
if (linearDepth < u_CascadeSplits.z) return 2;
return 3;
}
mat4 getLightSpaceMatrix(int cascade)
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
{
if (cascade == 0) return u_LightSpaceMatrix0;
else if (cascade == 1) return u_LightSpaceMatrix1;
else if (cascade == 2) return u_LightSpaceMatrix2;
else return u_LightSpaceMatrix3;
}
// Perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
float getCascadeSplit(int cascade)
{
if (cascade == 0) return u_CascadeSplits.x;
else if (cascade == 1) return u_CascadeSplits.y;
else if (cascade == 2) return u_CascadeSplits.z;
else return u_CascadeSplits.w;
}
// Transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
float sampleShadow(int cascade, vec2 uv, float compareDepth, float bias)
{
float depth;
if (cascade == 0) depth = texture(u_ShadowMap[0], uv).r;
else if (cascade == 1) depth = texture(u_ShadowMap[1], uv).r;
else if (cascade == 2) depth = texture(u_ShadowMap[2], uv).r;
else depth = texture(u_ShadowMap[3], uv).r;
return (compareDepth - bias) > depth ? 1.0 : 0.0;
}
// If outside shadow map bounds, assume no shadow
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
vec2 getTexelSize(int cascade)
{
if (cascade == 0) return 1.0 / vec2(textureSize(u_ShadowMap[0], 0));
else if (cascade == 1) return 1.0 / vec2(textureSize(u_ShadowMap[1], 0));
else if (cascade == 2) return 1.0 / vec2(textureSize(u_ShadowMap[2], 0));
else return 1.0 / vec2(textureSize(u_ShadowMap[3], 0));
}
// Get closest depth value from light's perspective
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float pcfShadow(int cascade, vec3 projCoords, float bias)
{
vec2 texelSize = getTexelSize(cascade);
int pcfRange = clamp(int(u_ShadowSoftness), 0, 3);
// Calculate bias based on surface angle
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
// PCF (Percentage Closer Filtering) for soft shadows
float shadow = 0.0;
int samples = 0;
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
int pcfRange = int(u_ShadowSoftness);
int sampleCount = 0;
for (int x = -pcfRange; x <= pcfRange; ++x)
for(int x = -pcfRange; x <= pcfRange; ++x)
{
for (int y = -pcfRange; y <= pcfRange; ++y)
for(int y = -pcfRange; y <= pcfRange; ++y)
{
vec2 offset = vec2(x, y) * texelSize;
shadow += sampleShadow(cascade, projCoords.xy + offset, projCoords.z, bias);
samples++;
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
sampleCount++;
}
}
return shadow / float(samples);
shadow /= float(sampleCount);
return shadow;
}
float calculateCSMShadow(vec3 worldPos, vec3 normal, vec3 lightDir, float viewZ)
{
int cascade = selectCascade(viewZ);
vec4 fragPosLightSpace = getLightSpaceMatrix(cascade) * vec4(worldPos, 1.0);
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
{
if (u_ShadowEnabled == 0) return 1.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0) return 1.0;
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
float shadow = pcfShadow(cascade, projCoords, bias);
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float cascadeEdge = 0.0;
if (cascade < CSM_CASCADE_COUNT - 1)
{
float depth = -viewZ;
float linearDepth = (depth - u_ShadowNear) / (u_ShadowFar - u_ShadowNear);
float splitDist = getCascadeSplit(cascade);
float prevSplit = (cascade > 0) ? getCascadeSplit(cascade - 1) : 0.0;
float blendStart = splitDist - (splitDist - prevSplit) * 0.1;
if (linearDepth > blendStart)
{
cascadeEdge = (linearDepth - blendStart) / (splitDist - blendStart);
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
int nextCascade = cascade + 1;
vec4 fragPosNext = getLightSpaceMatrix(nextCascade) * vec4(worldPos, 1.0);
vec3 projCoordsNext = fragPosNext.xyz / fragPosNext.w;
projCoordsNext = projCoordsNext * 0.5 + 0.5;
if (!(projCoordsNext.z > 1.0 || projCoordsNext.x < 0.0 || projCoordsNext.x > 1.0 ||
projCoordsNext.y < 0.0 || projCoordsNext.y > 1.0))
{
float nextShadow = pcfShadow(nextCascade, projCoordsNext, bias);
shadow = mix(shadow, nextShadow, cascadeEdge);
}
}
}
return shadow * u_ShadowIntensity;
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
}
void main()
{
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord) : u_AlbedoColor;
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05);
// === 1. 采样基础属性(所有模式都需要) ===
vec4 albedoWithAlpha = texture(u_AlbedoTexture, vs_Input.TexCoord);
vec3 albedo = u_AlbedoTexToggle > 0.5 ? albedoWithAlpha.rgb : u_AlbedoColor;
float alpha = u_AlbedoTexToggle > 0.5 ? albedoWithAlpha.a : 1.0;
float ao = u_AOTexToggle > 0.5 ? texture(u_AOTexture, vs_Input.TexCoord).r : u_AO;
float metallic = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
float roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
roughness = max(roughness, 0.05);
// normal
m_Params.Normal = normalize(vs_Input.Normal);
// === 2. 法线计算(世界空间) ===
vec3 normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
vec3 tangentNormal = texture(u_NormalTexture, vs_Input.TexCoord).rgb * 2.0 - 1.0;
normal = normalize(vs_Input.WorldNormals * tangentNormal);
}
// === 3. 自发光计算 ===
vec3 emissive = u_EmissiveColor;
if (u_EmissiveTexToggle > 0.5)
emissive = texture(u_EmissiveTexture, vs_Input.TexCoord).rgb;
emissive *= u_EmissiveIntensity;
// === 4. GBuffer 模式:直接输出到多个目标 ===
if (u_GBufferMode)
{
outAlbedoMetal = vec4(albedo, metallic);
outNormalRoughness = vec4(normal * 0.5 + 0.5, roughness);
outEmissiveAO = vec4(emissive, 1.0); // AO 暂设为 1.0
return; // 提前结束
}
// === 5. 非 GBuffer 模式:继续 PBR 光照计算 ===
// 填充 PBRParameters
m_Params.Albedo = albedo;
m_Params.Metalness = metallic;
m_Params.Roughness = roughness;
m_Params.Normal = normal;
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
vec3 F0 = mix(Fdielectric, m_Params.Albedo.rgb, m_Params.Metalness);
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
// Shadow
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0.5) {
float shadow = calculateCSMShadow(vs_Input.WorldPosition, m_Params.Normal, u_DirectionalLights.Direction, vs_Input.ViewZ);
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
shadowFactor = 1.0 - shadow;
}
// directional light with with shadow
vec3 lightContribution = u_DirectionalLights.Intensity > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
if(u_PointLightCount > 0)
{
for( int i = 0; i < u_PointLightCount; i ++) {
lightContribution += ComputePointLight(u_PointLights[i], F0, m_Params, vs_Input.WorldPosition);
}
}
lightContribution += ComputePointLight(u_PointLights, F0, m_Params, vs_Input.WorldPosition);
if(u_SpotLightCount > 0)
{
for(int i = 0; i < u_SpotLightCount; i ++)
{
lightContribution += ComputeSpotLight(u_SpotLights[i], F0, m_Params, vs_Input.WorldPosition);
}
}
lightContribution += ComputeSpotLight(u_SpotLights, F0, m_Params, vs_Input.WorldPosition);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution * ao;
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
vec3 finalRGB = lightContribution + iblContribution + emissive;
vec4 finalColor = vec4(finalRGB, alpha);
color = vec4(lightContribution + iblContribution, 1.0);
outColor = finalColor;
// Bloom
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
o_BloomColor = brightness > u_BloomThreshold ? color : vec4(0.0, 0.0, 0.0, 1.0);
o_MaterialInfo = vec4(m_Params.Metalness, m_Params.Roughness, 0.0, 1.0);
float brightness = dot(finalColor.rgb, vec3(0.2126, 0.7152, 0.0722));
outBloomColor = brightness > u_BloomThreshold ? finalColor : vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@ -1,178 +0,0 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2D u_ColorTexture;
uniform sampler2D u_DepthTexture;
uniform sampler2D u_MaterialInfoTexture;
uniform mat4 u_Projection;
uniform mat4 u_View;
uniform mat4 u_InvViewProjection;
uniform vec3 u_CameraPosition;
uniform vec2 u_ScreenSize;
uniform float u_CameraNear;
uniform float u_CameraFar;
uniform int u_Steps = 32;
uniform float u_Thickness = 0.5;
uniform float u_MaxDistance = 30.0;
uniform float u_StepSize = 0.5;
uniform float u_RayOffset = 0.1;
uniform float u_Intensity = 1.0;
float saturate(float x)
{
return clamp(x, 0.0, 1.0);
}
float LinearizeDepth(float depth)
{
float z = depth * 2.0 - 1.0;
return (2.0 * u_CameraNear * u_CameraFar) / (u_CameraFar + u_CameraNear - z * (u_CameraFar - u_CameraNear));
}
vec3 ComputeWorldSpacePosition(vec2 uv, float depth)
{
vec4 clipPos = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec4 worldPos = u_InvViewProjection * clipPos;
return worldPos.xyz / worldPos.w;
}
vec3 ReconstructNormalFromDepth(vec2 tc, float depth)
{
vec2 texelSize = 1.0 / u_ScreenSize;
vec2 tcL = tc - vec2(texelSize.x, 0.0);
vec2 tcR = tc + vec2(texelSize.x, 0.0);
vec2 tcB = tc - vec2(0.0, texelSize.y);
vec2 tcT = tc + vec2(0.0, texelSize.y);
float depthL = texture(u_DepthTexture, tcL).r;
float depthR = texture(u_DepthTexture, tcR).r;
float depthB = texture(u_DepthTexture, tcB).r;
float depthT = texture(u_DepthTexture, tcT).r;
vec3 pC = ComputeWorldSpacePosition(tc, depth);
vec3 pR = ComputeWorldSpacePosition(tcR, depthR);
vec3 pT = ComputeWorldSpacePosition(tcT, depthT);
vec3 dx = pR - pC;
vec3 dy = pT - pC;
return normalize(cross(dx, dy));
}
vec3 ComputeNDCWithZ(vec3 worldPos)
{
vec4 clipPos = u_Projection * u_View * vec4(worldPos, 1.0);
vec3 ndc = clipPos.xyz / clipPos.w;
ndc.xy = ndc.xy * 0.5 + 0.5;
return ndc;
}
void main()
{
float depth = texture(u_DepthTexture, v_TexCoord).r;
if (depth >= 1.0)
{
o_Color = vec4(0.0);
return;
}
vec4 matInfo = texture(u_MaterialInfoTexture, v_TexCoord);
float metalness = matInfo.r;
float roughness = matInfo.g;
if (roughness > 0.9)
{
o_Color = vec4(0.0);
return;
}
vec3 positionWS = ComputeWorldSpacePosition(v_TexCoord, depth);
vec3 viewWS = normalize(u_CameraPosition - positionWS);
vec3 normalWS = ReconstructNormalFromDepth(v_TexCoord, depth);
vec3 reflectionWS = normalize(reflect(-viewWS, normalWS));
if (dot(reflectionWS, normalWS) < 0.0)
{
o_Color = vec4(0.0);
return;
}
vec3 rayWS = positionWS + normalWS * u_RayOffset;
for (int i = 0; i < u_Steps; i++)
{
rayWS += reflectionWS * u_StepSize;
vec3 rayNDC = ComputeNDCWithZ(rayWS);
float rayDepth = rayNDC.z;
vec2 raySS = rayNDC.xy * u_ScreenSize;
if (rayNDC.x < 0.0 || rayNDC.x > 1.0 || rayNDC.y < 0.0 || rayNDC.y > 1.0)
break;
float sceneDepth = texture(u_DepthTexture, rayNDC.xy).r;
float sceneDepthLinear = LinearizeDepth(sceneDepth);
float rayDepthLinear = LinearizeDepth(rayDepth * 0.5 + 0.5);
float delta = rayDepthLinear - sceneDepthLinear;
if (delta >= 0.0 && delta <= u_Thickness)
{
vec3 hitNormalWS = ReconstructNormalFromDepth(rayNDC.xy, sceneDepth);
if (dot(hitNormalWS, reflectionWS) > 0.0)
break;
float ssrStrength = saturate(1.0 - roughness * roughness);
float dist = distance(positionWS, rayWS);
float fadeFactor = 1.0 - smoothstep(u_MaxDistance * 0.3, u_MaxDistance, dist);
float edgeFade = 1.0;
edgeFade *= smoothstep(0.0, 0.1, rayNDC.x);
edgeFade *= smoothstep(0.0, 0.1, rayNDC.y);
edgeFade *= smoothstep(0.0, 0.1, 1.0 - rayNDC.x);
edgeFade *= smoothstep(0.0, 0.1, 1.0 - rayNDC.y);
float NdotV = max(dot(normalWS, viewWS), 0.0);
vec3 F0 = mix(vec3(0.04), vec3(1.0), metalness);
vec3 Fresnel = F0 + (1.0 - F0) * pow(1.0 - NdotV, 5.0);
ssrStrength *= fadeFactor * edgeFade * u_Intensity;
vec4 color = texture(u_ColorTexture, rayNDC.xy);
color.rgb *= ssrStrength * Fresnel;
color.a = ssrStrength * dot(Fresnel, vec3(0.333));
o_Color = color;
return;
}
}
o_Color = vec4(0.0);
}

View File

@ -1,85 +0,0 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2D u_SSRTexture;
uniform sampler2D u_DepthTexture;
uniform sampler2D u_MaterialInfoTexture;
uniform vec2 u_ScreenSize;
uniform float u_BlurRadius = 4.0;
void main()
{
vec2 texelSize = 1.0 / u_ScreenSize;
vec4 center = texture(u_SSRTexture, v_TexCoord);
if (center.a <= 0.001)
{
o_Color = vec4(0.0);
return;
}
float roughness = texture(u_MaterialInfoTexture, v_TexCoord).g;
float effectiveBlurRadius = max(u_BlurRadius * roughness * roughness, 1.0);
float centerDepth = texture(u_DepthTexture, v_TexCoord).r;
vec3 result = vec3(0.0);
float totalWeight = 0.0;
const int kRadius = 6;
float sigma = effectiveBlurRadius * 0.5;
for (int x = -kRadius; x <= kRadius; x++)
{
for (int y = -kRadius; y <= kRadius; y++)
{
vec2 offset = vec2(float(x), float(y)) * texelSize * effectiveBlurRadius / float(kRadius);
vec2 sampleUV = v_TexCoord + offset;
if (sampleUV.x < 0.0 || sampleUV.x > 1.0 || sampleUV.y < 0.0 || sampleUV.y > 1.0)
continue;
float sampleDepth = texture(u_DepthTexture, sampleUV).r;
float depthDiff = abs(centerDepth - sampleDepth);
float depthWeight = exp(-depthDiff * 200.0);
float spatialWeight = exp(-(float(x * x + y * y)) / (2.0 * sigma * sigma));
vec4 sampleColor = texture(u_SSRTexture, sampleUV);
float alphaWeight = sampleColor.a > 0.01 ? 1.0 : 0.0;
float weight = spatialWeight * depthWeight * alphaWeight;
result += sampleColor.rgb * weight;
totalWeight += weight;
}
}
if (totalWeight > 0.0)
result /= totalWeight;
else
result = center.rgb;
o_Color = vec4(result, center.a);
}

View File

@ -17,25 +17,11 @@ void main()
#version 430
layout(location = 0) out vec4 o_Color;
layout(location = 1) out vec4 o_BloomTexture;
in vec2 v_TexCoord;
uniform sampler2DMS u_Texture;
uniform sampler2DMS u_DepthTexture;
uniform sampler2D u_BloomTexture;
uniform mat4 u_InvProjection;
uniform mat4 u_InvView;
// Fog
uniform float u_FogEnabled;
uniform vec3 u_FogColor;
uniform float u_FogDensity; // 雾密度,典型值 0.01~0.05
uniform float u_FogHeight; // 雾的起始高度(世界 Y 坐标),低于此高度雾最浓
uniform float u_FogHeightFalloff; // 高度衰减系数,值越大高度影响越剧烈(典型 0.5~2.0
uniform sampler2D u_HDRTexture; // 来自 LightingPass 的 HDR 颜色
uniform sampler2D u_BloomTexture; // 来自 BloomBlendPass 的 Bloom 纹理
uniform bool u_EnableAutoExposure;
uniform float u_ManualExposure;
@ -44,132 +30,36 @@ layout(std430, binding = 2) buffer Exposure
float u_Exposure;
};
uniform int u_TextureSamples;
uniform bool u_EnableBloom;
uniform float u_BloomThreshold;
uniform bool u_EnableSSR;
uniform sampler2D u_SSRTexture;
const float uFar = 1.0;
vec4 SampleTexture(sampler2D tex, vec2 texCoord)
{
return texture(tex, texCoord);
}
vec4 MultiSampleTexture(sampler2DMS tex, vec2 tc)
{
ivec2 texSize = textureSize(tex);
ivec2 texCoord = ivec2(tc * texSize);
vec4 result = vec4(0.0);
for (int i = 0; i < u_TextureSamples; i++)
result += texelFetch(tex, texCoord, i);
result /= float(u_TextureSamples);
return result;
}
float MultiSampleDepth(sampler2DMS tex, vec2 tc)
{
ivec2 texSize = textureSize(tex);
ivec2 texCoord = ivec2(tc * texSize);
float result = 0.0;
for (int i = 0; i < u_TextureSamples; i++)
result += texelFetch(tex, texCoord, i).r;
result /= float(u_TextureSamples);
return result;
}
const float gamma = 2.2;
const float pureWhite = 1.0;
void main()
{
const float gamma = 2.2;
// Tonemapping
vec4 msColor = MultiSampleTexture(u_Texture, v_TexCoord);
vec3 color = msColor.rgb;
// 采样 HDR 颜色(单样本)
vec3 color = texture(u_HDRTexture, v_TexCoord).rgb;
// 混合 Bloom如果启用
if (u_EnableBloom)
{
vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb;
color += bloomColor;
color += bloomColor; // 在 HDR 空间混合
}
if (u_EnableSSR)
{
vec4 ssrSample = texture(u_SSRTexture, v_TexCoord);
color = mix(color, ssrSample.rgb, ssrSample.a);
}
float exposure = 1.0f;
if(u_EnableAutoExposure)
exposure = u_Exposure;
// 应用曝光
if (u_EnableAutoExposure)
color *= u_Exposure;
else
exposure = u_ManualExposure;
color *= exposure;
color *= u_ManualExposure;
vec3 mappedColor = color;
// Reinhard 色调映射
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
float mappedLuminance = (luminance * (1.0 + luminance / (pureWhite * pureWhite))) / (1.0 + luminance);
float luminance = dot(mappedColor, vec3(0.2126, 0.7152, 0.0722));
if (luminance > 0.00001)
{
float a = 2.51;
float b = 0.03;
float c = 2.43;
float d = 0.59;
float e = 0.14;
mappedColor = clamp((mappedColor * (a * mappedColor + b)) / (mappedColor * (c * mappedColor + d) + e), 0.0, 1.0);
}
// 按亮度比例缩放颜色
vec3 mappedColor = (mappedLuminance / luminance) * color;
if (u_FogEnabled > 0.5)
{
// 1. 获取深度并重建视空间坐标
float depth = MultiSampleDepth(u_DepthTexture, v_TexCoord);
vec4 clipPos = vec4(v_TexCoord * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec4 viewPos4 = u_InvProjection * clipPos;
viewPos4 /= viewPos4.w;
vec3 viewPos = viewPos4.xyz; // 视空间坐标相机为原点Z 轴向内)
// 2. 距离雾因子(指数雾)
float distance = length(viewPos);
float distanceFactor = 1.0 - exp(-distance * u_FogDensity);
// 可选:指数平方雾(更平滑)
// float distanceFactor = 1.0 - exp(-pow(distance * u_FogDensity, 2.0));
// 3. 高度雾因子(基于世界 Y
// 将视空间坐标转换到世界空间
vec4 worldPos4 = u_InvView* vec4(viewPos, 1.0);
vec3 worldPos = worldPos4.xyz / worldPos4.w;
float worldY = worldPos.y;
float heightFactor = 0.0;
if (worldY < u_FogHeight)
{
// 低于起始高度:雾最浓,因子为 1
heightFactor = 1.0;
}
else
{
// 高于起始高度:指数衰减,高度越高雾越淡
float deltaY = worldY - u_FogHeight;
heightFactor = exp(-deltaY * u_FogHeightFalloff);
}
// 可选:使用 clamp 限制范围
heightFactor = clamp(heightFactor, 0.0, 1.0);
// 4. 综合雾因子(可相乘,也可取最大值,通常相乘)
float fogFactor = distanceFactor * heightFactor;
// 5. 混合雾颜色(雾颜色直接使用 u_FogColor已在 LDR 空间)
mappedColor = mix(mappedColor, u_FogColor, fogFactor);
}
// Gamma correction.
// Gamma 校正
o_Color = vec4(pow(mappedColor, vec3(1.0 / gamma)), 1.0);
// Show over-exposed areas
// if (o_Color.r > 1.0 || o_Color.g > 1.0 || o_Color.b > 1.0)
// o_Color.rgb *= vec3(1.0, 0.25, 0.25);
}
}

View File

@ -1,6 +1,6 @@
[Window][DockSpace Demo]
Pos=0,0
Size=1920,1080
Size=2560,1566
Collapsed=0
[Window][Debug##Default]
@ -9,32 +9,32 @@ Size=400,400
Collapsed=0
[Window][Scene Hierarchy]
Pos=1449,24
Size=471,385
Pos=2089,24
Size=471,563
Collapsed=0
DockId=0x00000009,0
[Window][Properties]
Pos=1449,411
Size=471,669
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,0
[Window][Scene Renderer]
Pos=0,585
Size=481,495
Pos=0,843
Size=481,723
Collapsed=0
DockId=0x00000006,0
[Window][Materials]
Pos=0,24
Size=481,559
Size=481,817
Collapsed=0
DockId=0x00000005,0
[Window][Script Engine Debug]
Pos=1449,411
Size=471,669
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,2
@ -52,25 +52,25 @@ DockId=0x00000001,0
[Window][Viewport]
Pos=483,58
Size=964,647
Size=1604,955
Collapsed=0
DockId=0x0000000B,0
[Window][Environment]
Pos=1449,411
Size=471,669
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,1
[Window][Project]
Pos=483,707
Size=964,373
Pos=483,1015
Size=1604,551
Collapsed=0
DockId=0x0000000C,0
[Window][Objects]
Pos=483,707
Size=964,373
Pos=483,1015
Size=1604,551
Collapsed=0
DockId=0x0000000C,1
@ -81,18 +81,12 @@ Collapsed=0
[Window][##tool_bar]
Pos=483,24
Size=964,32
Size=1604,32
Collapsed=0
DockId=0x00000001,0
[Window][Console]
Pos=483,707
Size=964,373
Collapsed=0
DockId=0x0000000C,2
[Docking][Data]
DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=268,189 Size=1920,1056 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

Some files were not shown because too many files have changed in this diff Show More