diff --git a/Editor/Editor/EditorLayer.cpp b/Editor/Editor/EditorLayer.cpp index e53f37e..6aee7e9 100644 --- a/Editor/Editor/EditorLayer.cpp +++ b/Editor/Editor/EditorLayer.cpp @@ -15,9 +15,12 @@ #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" @@ -55,6 +58,7 @@ namespace Prism SceneRenderer::GetOptions().ShowGrid = true; AssetEditorPanel::RegisterDefaultEditors(); + PBRMaterialEditor::InitPreviewRenderer(); FileSystem::StartWatching(); m_ConsolePanel = CreateScope(); @@ -73,6 +77,8 @@ 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 }); @@ -166,6 +172,25 @@ namespace Prism } } } + + auto editingAsset = PBRMaterialEditor::GetEditingAsset(); + if (editingAsset && (editingAsset->PreviewIsDirty || !editingAsset->PreviewTexture)) + { + PBRMaterialEditor::RenderMaterialPreview(editingAsset); + } + else + { + bool renderedPreviewThisFrame = false; + AssetsManager::ForEachAsset([&](Ref mat) + { + if (renderedPreviewThisFrame) return; + if (mat->PreviewIsDirty || !mat->PreviewTexture) + { + PBRMaterialEditor::RenderMaterialPreview(mat); + renderedPreviewThisFrame = true; + } + }); + } } void EditorLayer::OnImGuiRender() @@ -1484,6 +1509,14 @@ namespace Prism m_EditorCamera = EditorCamera(glm::perspectiveFov(glm::radians(45.0f), 1280.0f, 720.0f, 0.1f, 1000.0f)); m_SelectionContext.clear(); + + // new Scene with a default sky light + auto newEntity = m_EditorScene->CreateEntity("Sky Light"); + auto& slc = newEntity.AddComponent(); + slc.DynamicSky = true; + + Ref preethamEnv = Renderer3D::CreatePreethamSky(slc.TurbidityAzimuthInclination); + slc.SceneEnvironment = Ref::Create(preethamEnv, preethamEnv); } void EditorLayer::OpenScene() diff --git a/Editor/assets/editor/model.png b/Editor/assets/editor/model.png new file mode 100644 index 0000000..09e1291 Binary files /dev/null and b/Editor/assets/editor/model.png differ diff --git a/Editor/assets/editor/texture.png b/Editor/assets/editor/texture.png new file mode 100644 index 0000000..f3cfe0a Binary files /dev/null and b/Editor/assets/editor/texture.png differ diff --git a/Editor/assets/shaders/PBRShader_Anim.glsl b/Editor/assets/shaders/PBRShader_Anim.glsl index 89eacd7..cf6c6d7 100644 --- a/Editor/assets/shaders/PBRShader_Anim.glsl +++ b/Editor/assets/shaders/PBRShader_Anim.glsl @@ -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,8 +28,6 @@ uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ViewMatrix; uniform mat4 u_Transform; -uniform mat4 u_LightSpaceMatrix; - out VertexOutput { vec3 WorldPosition; @@ -39,7 +37,7 @@ out VertexOutput mat3 WorldTransform; vec3 Binormal; vec3 ViewPosition; - vec4 FragPosLightSpace; + float ViewZ; } vs_Output; void main() @@ -58,9 +56,8 @@ 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; @@ -76,7 +73,8 @@ const int LightCount = 1; const vec3 Fdielectric = vec3(0.04); layout(location = 0) out vec4 color; -layout(location = 1) out vec4 o_BloomColor; +layout(location = 1) out vec4 o_MaterialInfo; +layout(location = 2) out vec4 o_BloomColor; struct DirectionalLight { vec3 Direction; @@ -113,7 +111,7 @@ in VertexOutput mat3 WorldTransform; vec3 Binormal; vec3 ViewPosition; - vec4 FragPosLightSpace; + float ViewZ; } vs_Input; @@ -156,7 +154,15 @@ uniform float u_MetalnessTexToggle; uniform float u_RoughnessTexToggle; // shadow -uniform sampler2D u_ShadowMap; +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 float u_ShadowBias; uniform float u_ShadowSoftness; uniform float u_ShadowIntensity; @@ -362,23 +368,54 @@ vec3 IBL(vec3 F0, vec3 Lr) } // shadow -float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) +int selectCascade(float viewZ) { - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; + 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; +} - if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || - projCoords.y < 0.0 || projCoords.y > 1.0) - return 0.0; +mat4 getLightSpaceMatrix(int cascade) +{ + if (cascade == 0) return u_LightSpaceMatrix0; + else if (cascade == 1) return u_LightSpaceMatrix1; + else if (cascade == 2) return u_LightSpaceMatrix2; + else return u_LightSpaceMatrix3; +} - float closestDepth = texture(u_ShadowMap, projCoords.xy).r; - float currentDepth = projCoords.z; +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; +} - float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1); +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; +} +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)); +} + +float pcfShadow(int cascade, vec3 projCoords, float bias) +{ + vec2 texelSize = getTexelSize(cascade); int pcfRange = clamp(int(u_ShadowSoftness), 0, 3); - - vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0); float shadow = 0.0; int samples = 0; @@ -387,34 +424,57 @@ float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) for (int y = -pcfRange; y <= pcfRange; ++y) { vec2 offset = vec2(x, y) * texelSize; - float pcfDepth = texture(u_ShadowMap, projCoords.xy + offset).r; - shadow += (currentDepth - bias) > pcfDepth ? 1.0 : 0.0; + shadow += sampleShadow(cascade, projCoords.xy + offset, projCoords.z, bias); samples++; } } - shadow /= float(samples); - return shadow * u_ShadowIntensity; + return shadow / float(samples); } - -float ComputeShadow(vec4 fragPosLightSpace, float NdotL) +float calculateCSMShadow(vec3 worldPos, vec3 normal, vec3 lightDir, float viewZ) { - if (u_ShadowEnabled == 0) return 1.0; + int cascade = selectCascade(viewZ); + + vec4 fragPosLightSpace = getLightSpaceMatrix(cascade) * vec4(worldPos, 1.0); vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x < 0.0 || projCoords.x > 1.0 || - projCoords.y < 0.0 || projCoords.y > 1.0 || - projCoords.z > 1.0) return 1.0; + 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 = pcfShadow(cascade, projCoords, bias); - float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5); + 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 shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0; - return mix(1.0, 1.0 - u_ShadowIntensity, shadow); + 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; } @@ -442,7 +502,7 @@ void main() float shadowFactor = 1.0; if (u_ShadowEnabled > 0.5) { - float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction); + float shadow = calculateCSMShadow(vs_Input.WorldPosition, m_Params.Normal, u_DirectionalLights.Direction, vs_Input.ViewZ); shadowFactor = 1.0 - shadow; } @@ -471,4 +531,6 @@ void main() // 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); } diff --git a/Editor/assets/shaders/PBRShader_Static.glsl b/Editor/assets/shaders/PBRShader_Static.glsl index 63c0082..0fd449e 100644 --- a/Editor/assets/shaders/PBRShader_Static.glsl +++ b/Editor/assets/shaders/PBRShader_Static.glsl @@ -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,8 +22,6 @@ uniform mat4 u_ViewProjectionMatrix; uniform mat4 u_ViewMatrix; uniform mat4 u_Transform; -uniform mat4 u_LightSpaceMatrix; - out VertexOutput { vec3 WorldPosition; @@ -33,7 +31,7 @@ out VertexOutput mat3 WorldTransform; vec3 Binormal; vec3 ViewPosition; - vec4 FragPosLightSpace; + float ViewZ; } vs_Output; void main() @@ -45,9 +43,8 @@ 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); } @@ -62,7 +59,8 @@ const int LightCount = 1; const vec3 Fdielectric = vec3(0.04); layout(location = 0) out vec4 color; -layout(location = 1) out vec4 o_BloomColor; +layout(location = 1) out vec4 o_MaterialInfo; +layout(location = 2) out vec4 o_BloomColor; struct DirectionalLight { vec3 Direction; @@ -100,7 +98,7 @@ in VertexOutput mat3 WorldTransform; vec3 Binormal; vec3 ViewPosition; - vec4 FragPosLightSpace; + float ViewZ; } vs_Input; uniform DirectionalLight u_DirectionalLights; @@ -143,7 +141,15 @@ uniform float u_MetalnessTexToggle; uniform float u_RoughnessTexToggle; // shadow -uniform sampler2D u_ShadowMap; +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 float u_ShadowBias; uniform float u_ShadowSoftness; uniform float u_ShadowIntensity; @@ -338,7 +344,7 @@ vec3 IBL(vec3 F0, vec3 Lr) 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); @@ -347,23 +353,54 @@ vec3 IBL(vec3 F0, vec3 Lr) } // shadow -float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) +int selectCascade(float viewZ) { - vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; - projCoords = projCoords * 0.5 + 0.5; + 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; +} - if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || - projCoords.y < 0.0 || projCoords.y > 1.0) - return 0.0; +mat4 getLightSpaceMatrix(int cascade) +{ + if (cascade == 0) return u_LightSpaceMatrix0; + else if (cascade == 1) return u_LightSpaceMatrix1; + else if (cascade == 2) return u_LightSpaceMatrix2; + else return u_LightSpaceMatrix3; +} - float closestDepth = texture(u_ShadowMap, projCoords.xy).r; - float currentDepth = projCoords.z; +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; +} - float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1); +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; +} +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)); +} + +float pcfShadow(int cascade, vec3 projCoords, float bias) +{ + vec2 texelSize = getTexelSize(cascade); int pcfRange = clamp(int(u_ShadowSoftness), 0, 3); - - vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0); float shadow = 0.0; int samples = 0; @@ -372,34 +409,57 @@ float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) for (int y = -pcfRange; y <= pcfRange; ++y) { vec2 offset = vec2(x, y) * texelSize; - float pcfDepth = texture(u_ShadowMap, projCoords.xy + offset).r; - shadow += (currentDepth - bias) > pcfDepth ? 1.0 : 0.0; + shadow += sampleShadow(cascade, projCoords.xy + offset, projCoords.z, bias); samples++; } } - shadow /= float(samples); - return shadow * u_ShadowIntensity; + return shadow / float(samples); } - -float ComputeShadow(vec4 fragPosLightSpace, float NdotL) +float calculateCSMShadow(vec3 worldPos, vec3 normal, vec3 lightDir, float viewZ) { - if (u_ShadowEnabled == 0) return 1.0; + int cascade = selectCascade(viewZ); + + vec4 fragPosLightSpace = getLightSpaceMatrix(cascade) * vec4(worldPos, 1.0); vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; projCoords = projCoords * 0.5 + 0.5; - if (projCoords.x < 0.0 || projCoords.x > 1.0 || - projCoords.y < 0.0 || projCoords.y > 1.0 || - projCoords.z > 1.0) return 1.0; + 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 = pcfShadow(cascade, projCoords, bias); - float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5); + 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 shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0; - return mix(1.0, 1.0 - u_ShadowIntensity, shadow); + 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; } @@ -428,7 +488,7 @@ void main() // Shadow float shadowFactor = 1.0; if (u_ShadowEnabled > 0.5) { - float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction); + float shadow = calculateCSMShadow(vs_Input.WorldPosition, m_Params.Normal, u_DirectionalLights.Direction, vs_Input.ViewZ); shadowFactor = 1.0 - shadow; } @@ -459,4 +519,6 @@ void main() // 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); } diff --git a/Editor/assets/shaders/SSR.glsl b/Editor/assets/shaders/SSR.glsl new file mode 100644 index 0000000..e23a03a --- /dev/null +++ b/Editor/assets/shaders/SSR.glsl @@ -0,0 +1,207 @@ +#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_InvProjection; + +uniform vec2 u_ScreenSize; +uniform float u_CameraNear; +uniform float u_CameraFar; + +uniform int u_Steps = 64; +uniform float u_Thickness = 0.5; +uniform float u_MaxDistance = 30.0; +uniform float u_Intensity = 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 ReconstructViewPos(vec2 tc, float depth) +{ + float z = depth * 2.0 - 1.0; + vec4 clipPos = vec4(tc * 2.0 - 1.0, z, 1.0); + vec4 viewPos4 = u_InvProjection * clipPos; + return viewPos4.xyz / viewPos4.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 pL = ReconstructViewPos(tcL, depthL); + vec3 pR = ReconstructViewPos(tcR, depthR); + vec3 pB = ReconstructViewPos(tcB, depthB); + vec3 pT = ReconstructViewPos(tcT, depthT); + + vec3 dx = pR - pL; + vec3 dy = pT - pB; + + return normalize(cross(dx, dy)); +} + +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 viewPos = ReconstructViewPos(v_TexCoord, depth); + vec3 normal = ReconstructNormalFromDepth(v_TexCoord, depth); + + vec3 viewDir = normalize(-viewPos); + float NdotV = max(dot(normal, viewDir), 0.0); + + vec3 reflectDir = reflect(-viewDir, normal); + + if (dot(reflectDir, normal) < 0.0) + { + o_Color = vec4(0.0); + return; + } + + vec3 rayOrigin = viewPos + normal * 0.02; + vec3 rayEnd = rayOrigin + reflectDir * u_MaxDistance; + + vec4 p0 = u_Projection * vec4(rayOrigin, 1.0); + vec4 p1 = u_Projection * vec4(rayEnd, 1.0); + + float k0 = 1.0 / p0.w; + float k1 = 1.0 / p1.w; + + p0.xyz *= k0; + p1.xyz *= k1; + + vec3 v0 = rayOrigin * k0; + vec3 v1 = rayEnd * k1; + + vec2 s0 = p0.xy * 0.5 + 0.5; + vec2 s1 = p1.xy * 0.5 + 0.5; + + vec2 sDelta = s1 - s0; + float screenDist = length(sDelta * u_ScreenSize); + + if (screenDist < 1.0) + { + o_Color = vec4(0.0); + return; + } + + float divisions = min(screenDist, float(u_Steps)); + vec3 dV = (v1 - v0) / divisions; + float dK = (k1 - k0) / divisions; + vec2 dS = sDelta / divisions; + + vec3 curV = v0; + float curK = k0; + vec2 curS = s0; + + vec2 hitUV = vec2(0.0); + bool found = false; + + for (int i = 0; i < int(divisions); i++) + { + curV += dV; + curK += dK; + curS += dS; + + if (curS.x < 0.0 || curS.x > 1.0 || curS.y < 0.0 || curS.y > 1.0) + break; + + float sampleDepth = texture(u_DepthTexture, curS).r; + float surfDepth = LinearizeDepth(sampleDepth); + float rayDepth = -curV.z / curK; + + float depthDiff = rayDepth - surfDepth; + + if (depthDiff > 0.0 && depthDiff < u_Thickness) + { + hitUV = curS; + found = true; + break; + } + } + + if (!found) + { + o_Color = vec4(0.0); + return; + } + + vec3 hitNormal = ReconstructNormalFromDepth(hitUV, texture(u_DepthTexture, hitUV).r); + if (dot(hitNormal, -reflectDir) < 0.0) + { + o_Color = vec4(0.0); + return; + } + + vec3 reflectColor = texture(u_ColorTexture, hitUV).rgb; + + float dist = distance(viewPos, ReconstructViewPos(hitUV, texture(u_DepthTexture, hitUV).r)); + float fadeFactor = 1.0 - smoothstep(u_MaxDistance * 0.5, u_MaxDistance, dist); + + float edgeFade = 1.0; + edgeFade *= smoothstep(0.0, 0.15, hitUV.x); + edgeFade *= smoothstep(0.0, 0.15, hitUV.y); + edgeFade *= smoothstep(0.0, 0.15, 1.0 - hitUV.x); + edgeFade *= smoothstep(0.0, 0.15, 1.0 - hitUV.y); + + vec3 F0 = mix(vec3(0.04), reflectColor, metalness); + vec3 Fresnel = F0 + (1.0 - F0) * pow(1.0 - NdotV, 5.0); + + float roughnessFade = 1.0 - roughness * roughness; + + vec3 ssrContrib = reflectColor * Fresnel * fadeFactor * edgeFade * roughnessFade * u_Intensity; + float ssrAlpha = length(Fresnel) * fadeFactor * edgeFade * roughnessFade; + + o_Color = vec4(ssrContrib, ssrAlpha); +} diff --git a/Editor/assets/shaders/SSRBlur.glsl b/Editor/assets/shaders/SSRBlur.glsl new file mode 100644 index 0000000..1124477 --- /dev/null +++ b/Editor/assets/shaders/SSRBlur.glsl @@ -0,0 +1,85 @@ +#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); +} diff --git a/Editor/assets/shaders/SceneComposite.glsl b/Editor/assets/shaders/SceneComposite.glsl index 68caf74..0e90c2f 100644 --- a/Editor/assets/shaders/SceneComposite.glsl +++ b/Editor/assets/shaders/SceneComposite.glsl @@ -49,6 +49,9 @@ 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) @@ -94,6 +97,12 @@ void main() color += bloomColor; } + if (u_EnableSSR) + { + vec4 ssrSample = texture(u_SSRTexture, v_TexCoord); + color += ssrSample.rgb * ssrSample.a; + } + float exposure = 1.0f; if(u_EnableAutoExposure) diff --git a/Prism/src/Prism/Asset/Asset.h b/Prism/src/Prism/Asset/Asset.h index cbe57a8..1b87807 100644 --- a/Prism/src/Prism/Asset/Asset.h +++ b/Prism/src/Prism/Asset/Asset.h @@ -91,6 +91,7 @@ namespace Prism Ref RoughnessTexture; bool IsDirty = true; + bool PreviewIsDirty = true; Ref PreviewTexture; }; diff --git a/Prism/src/Prism/Asset/AssetSerializer.cpp b/Prism/src/Prism/Asset/AssetSerializer.cpp index 8573a5b..3c437ad 100644 --- a/Prism/src/Prism/Asset/AssetSerializer.cpp +++ b/Prism/src/Prism/Asset/AssetSerializer.cpp @@ -149,6 +149,7 @@ namespace Prism out << YAML::EndMap; PM_CORE_INFO("Save Asset change..."); + FileSystem::NotifyFileWriting(asset->FilePath); std::ofstream fout(asset->FilePath); fout << out.c_str(); } @@ -421,7 +422,9 @@ namespace Prism out << YAML::Key << "Type" << YAML::Value << (int)asset->Type; out << YAML::EndMap; - std::ofstream fout(asset->FilePath + ".meta"); + std::string metaPath = asset->FilePath + ".meta"; + FileSystem::NotifyFileWriting(metaPath); + std::ofstream fout(metaPath); fout << out.c_str(); } @@ -434,7 +437,9 @@ namespace Prism out << YAML::Key << "Type" << YAML::Value << (int)asset->Type; out << YAML::EndMap; - std::ofstream fout(asset->FilePath + ".meta"); + std::string metaPath = asset->FilePath + ".meta"; + FileSystem::NotifyFileWriting(metaPath); + std::ofstream fout(metaPath); fout << out.c_str(); } } \ No newline at end of file diff --git a/Prism/src/Prism/Asset/AssetsManager.cpp b/Prism/src/Prism/Asset/AssetsManager.cpp index 4ec18c7..2a2bb3f 100644 --- a/Prism/src/Prism/Asset/AssetsManager.cpp +++ b/Prism/src/Prism/Asset/AssetsManager.cpp @@ -13,8 +13,6 @@ #include #include "AssetSerializer.h" -#include "Prism/Core/Application.h" -#include "Prism/Core/KeyCodes.h" #include "Prism/Renderer/SceneEnvironment.h" #include "Prism/Script/ScriptEngine.h" #include "Prism/Utilities/StringUtils.h" @@ -237,7 +235,9 @@ namespace Prism asset->FileName = Utils::RemoveExtension(Utils::GetFilename(asset->FilePath)); asset->Extension = Utils::GetFilename(filename); asset->ParentDirectory = directoryHandle; - asset->Handle = std::hash()(asset->FilePath + std::to_string(Application::Get().GetTime())); + do { + asset->Handle = AssetHandle(); + } while (s_LoadedAssets.find(asset->Handle) != s_LoadedAssets.end()); asset->IsDataLoaded = true; s_LoadedAssets[asset->Handle] = asset; @@ -259,6 +259,21 @@ namespace Prism return s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end() && s_LoadedAssets[assetHandle]->Type == type; } + template + void AssetsManager::ForEachAsset(const std::function)>& callback) + { + for (auto& [handle, asset] : s_LoadedAssets) + { + if (asset->Type == AssetType::Material && asset->IsDataLoaded) + { + Ref typed = asset.As(); + if (typed) + callback(typed); + } + } + } + template PRISM_API void AssetsManager::ForEachAsset(const std::function)>&); + std::string AssetsManager::StripExtras(const std::string& filename) { @@ -384,10 +399,18 @@ namespace Prism { if (!e.IsDirectory) { - Ref asset = ImportAsset(e.FilePath, parentHandle); - if (asset->Type == AssetType::Script) - isCharpFile = true; - + AssetHandle existingHandle = GetAssetHandleFromFilePath(e.FilePath); + if (existingHandle && s_LoadedAssets[existingHandle]->IsDataLoaded) + { + if (s_AssetsChangeCallback) + s_AssetsChangeCallback(); + } + else + { + Ref asset = ImportAsset(e.FilePath, parentHandle); + if (asset->Type == AssetType::Script) + isCharpFile = true; + } } } break; @@ -454,28 +477,10 @@ namespace Prism s_AssetsChangeCallback(); } - AssetHandle AssetsManager::FindParentHandleInChildren(Ref& dir, const std::string& dirName) - { - if (dir->FileName == dirName) - return dir->Handle; - - for (const AssetHandle& childHandle : dir->ChildDirectories) - { - Ref child = GetAsset(childHandle); - AssetHandle dirHandle = FindParentHandleInChildren(child, dirName); - - if (IsAssetHandleValid(dirHandle)) - return dirHandle; - } - - return 0; - } - AssetHandle AssetsManager::FindParentHandle(const std::string& filepath) { - const std::vector parts = Utils::SplitString(filepath, "/\\"); - const std::string& parentFolder = parts[parts.size() - 2]; - Ref assetsDirectory = GetAsset(GetAssetHandleFromFilePath("assets")); - return FindParentHandleInChildren(assetsDirectory, parentFolder); + std::string parentPath = std::filesystem::path(filepath).parent_path().string(); + std::replace(parentPath.begin(), parentPath.end(), '\\', '/'); + return GetAssetHandleFromFilePath(parentPath); } } diff --git a/Prism/src/Prism/Asset/AssetsManager.h b/Prism/src/Prism/Asset/AssetsManager.h index 4c016c9..222b232 100644 --- a/Prism/src/Prism/Asset/AssetsManager.h +++ b/Prism/src/Prism/Asset/AssetsManager.h @@ -63,6 +63,9 @@ namespace Prism static bool IsAssetType(AssetHandle assetHandle, AssetType type); + template + static void ForEachAsset(const std::function)>& callback); + static std::string StripExtras(const std::string& filename); private: @@ -78,7 +81,6 @@ namespace Prism static void OnFileSystemChanged(FileSystemChangedEvent e); - static AssetHandle FindParentHandleInChildren(Ref& dir, const std::string& dirName); static AssetHandle FindParentHandle(const std::string& filepath); private: diff --git a/Prism/src/Prism/Core/Application.cpp b/Prism/src/Prism/Core/Application.cpp index 090a79c..fe12fbf 100644 --- a/Prism/src/Prism/Core/Application.cpp +++ b/Prism/src/Prism/Core/Application.cpp @@ -165,7 +165,11 @@ namespace Prism Renderer::Submit([=]() { glViewport(0, 0, width, height); }); auto& fbs = FrameBufferPool::GetGlobal()->GetAll(); for (auto& fb : fbs) + { + if (fb->GetSpecification().NoResize) + continue; fb->Resize(width, height); + } return false; } diff --git a/Prism/src/Prism/Editor/ContentBrowserPanel.cpp b/Prism/src/Prism/Editor/ContentBrowserPanel.cpp index 5cf2d06..7900daa 100644 --- a/Prism/src/Prism/Editor/ContentBrowserPanel.cpp +++ b/Prism/src/Prism/Editor/ContentBrowserPanel.cpp @@ -29,13 +29,19 @@ namespace Prism m_AssetIconMap["hdr"] = AssetsManager::GetAsset("assets/editor/file.png"); m_AssetIconMap["fbx"] = AssetsManager::GetAsset("assets/editor/fbx.png"); m_AssetIconMap["obj"] = AssetsManager::GetAsset("assets/editor/obj.png"); + m_AssetIconMap["fbx"] = AssetsManager::GetAsset("assets/editor/fbx.png"); + m_AssetIconMap["dae"] = AssetsManager::GetAsset("assets/editor/model.png"); m_AssetIconMap["wav"] = AssetsManager::GetAsset("assets/editor/wav.png"); m_AssetIconMap["cs"] = AssetsManager::GetAsset("assets/editor/csc.png"); m_AssetIconMap["png"] = AssetsManager::GetAsset("assets/editor/png.png"); + m_AssetIconMap["jpg"] = AssetsManager::GetAsset("assets/editor/texture.png"); + m_AssetIconMap["jepg"] = AssetsManager::GetAsset("assets/editor/texture.png"); + m_AssetIconMap["bmp"] = AssetsManager::GetAsset("assets/editor/texture.png"); + m_AssetIconMap["tga"] = AssetsManager::GetAsset("assets/editor/texture.png"); + m_AssetIconMap["scene"] = AssetsManager::GetAsset("assets/editor/scene.png"); m_AssetIconMap["blend"] = AssetsManager::GetAsset("assets/editor/blend.png"); - // TODO: get a logo for this project - m_AssetIconMap["scene"] = AssetsManager::GetAsset("assets/editor/asset.png"); + m_AssetIconMap["wav"] = AssetsManager::GetAsset("assets/editor/wav.png"); m_AssetIconMap["pmat"] = AssetsManager::GetAsset("assets/editor/asset.png"); diff --git a/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.cpp b/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.cpp index 89cf2d8..2104047 100644 --- a/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.cpp +++ b/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.cpp @@ -1,4 +1,4 @@ -// +// // Created by Atdunbg on 2026/2/14. // @@ -13,6 +13,7 @@ #include "Prism/Renderer/Renderer3D.h" #include "Prism/Renderer/RenderPass.h" #include "Prism/Renderer/RHI/RHICommandBuffer.h" +#include "Prism/Renderer/SceneRenderer.h" #include "Prism/Scene/Scene.h" namespace Prism @@ -22,9 +23,6 @@ namespace Prism ed::Config config; config.SettingsFile = ""; m_EditorContext = ed::CreateEditor(&config); - - // TODO: function don't work - // InitPreviewResources(); } PBRMaterialEditor::~PBRMaterialEditor() @@ -101,110 +99,110 @@ namespace Prism AssetSerializer::SerializeAsset(m_Asset); } m_Asset = static_cast>(asset); - LoadAssetToGraph(); // 加载资产数据到节点图 + s_EditingAsset = m_Asset; + LoadAssetToGraph(); } - void PBRMaterialEditor::InitPreviewResources() + static Ref s_PreviewRenderPass; + static Ref s_PreviewSphere; + static Ref s_PreviewBaseMaterial; + static Ref s_PreviewMaterialInstance; + static Ref s_PreviewEnvRadiance; + static Ref s_PreviewEnvIrradiance; + static constexpr float s_PreviewFOV = 45.0f; + static constexpr float s_PreviewNear = 0.1f; + static constexpr float s_PreviewFar = 100.0f; + static constexpr glm::vec3 s_PreviewCameraPos = glm::vec3(2.0f, 1.5f, 3.0f); + static constexpr glm::vec3 s_PreviewCameraTarget = glm::vec3(0.0f); + + void PBRMaterialEditor::InitPreviewRenderer() { - // 创建离屏 Framebuffer(例如 512x512) FramebufferSpecification fbSpec; fbSpec.Width = 512; fbSpec.Height = 512; fbSpec.NoResize = true; fbSpec.Attachments = { FramebufferTextureFormat::RGBA16F, FramebufferTextureFormat::DEPTH24STENCIL8 }; - fbSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; + fbSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; Ref previewFrameBuffer = FrameBuffer::Create(fbSpec); RenderPassSpecification rpSpec; rpSpec.TargetFramebuffer = previewFrameBuffer; - m_PreviewRenderPass = RenderPass::Create(rpSpec); + s_PreviewRenderPass = RenderPass::Create(rpSpec); - m_PreviewSphere = MeshFactory::CreateSphere(0.5f); + s_PreviewSphere = MeshFactory::CreateSphere(1.5f); - auto pbrShader = Shader::Create("assets/shaders/PBRShader_Static.glsl"); // 你的 PBR Shader 路径 - auto material = Material::Create(pbrShader); - m_PreviewMaterialInstance = MaterialInstance::Create(material); + auto pbrShader = Shader::Create("assets/shaders/PBRShader_Static.glsl"); + s_PreviewBaseMaterial = Material::Create(pbrShader); + s_PreviewMaterialInstance = MaterialInstance::Create(s_PreviewBaseMaterial); + + auto [radiance, irradiance] = Renderer3D::CreateEnvironmentMap("assets/env/birchwood_4k.hdr"); + s_PreviewEnvRadiance = radiance; + s_PreviewEnvIrradiance = irradiance; } - void PBRMaterialEditor::RenderPreview() - { - if (!m_Asset || !m_PreviewSphere) return; + Ref PBRMaterialEditor::s_EditingAsset; + + void PBRMaterialEditor::RenderMaterialPreview(Ref materialAsset) + { + if (!materialAsset || !s_PreviewSphere) return; + + auto matInst = s_PreviewMaterialInstance; + matInst->Set("u_AlbedoColor", materialAsset->AlbedoColor); + matInst->Set("u_Metalness", materialAsset->Metalness); + matInst->Set("u_Roughness", materialAsset->Roughness); + matInst->Set("u_AlbedoTexToggle", materialAsset->AlbedoTexToggle); + matInst->Set("u_NormalTexToggle", materialAsset->NormalTexToggle); + matInst->Set("u_MetalnessTexToggle", materialAsset->MetalnessTexToggle); + matInst->Set("u_RoughnessTexToggle", materialAsset->RoughnessTexToggle); + + matInst->Set("u_AlbedoTexture", materialAsset->AlbedoTexture); + matInst->Set("u_NormalTexture", materialAsset->NormalTexture); + matInst->Set("u_MetalnessTexture", materialAsset->MetalnessTexture); + matInst->Set("u_RoughnessTexture", materialAsset->RoughnessTexture); + + float aspect = (float)s_PreviewRenderPass->GetSpecification().TargetFramebuffer->GetWidth() / + (float)s_PreviewRenderPass->GetSpecification().TargetFramebuffer->GetHeight(); + glm::mat4 projection = glm::perspective(glm::radians(s_PreviewFOV), aspect, s_PreviewNear, s_PreviewFar); + glm::mat4 view = glm::lookAt(s_PreviewCameraPos, s_PreviewCameraTarget, glm::vec3(0,1,0)); + glm::mat4 viewProjection = projection * view; + + auto baseMaterial = s_PreviewBaseMaterial; + baseMaterial->Set("u_ViewProjectionMatrix", viewProjection); + baseMaterial->Set("u_ViewMatrix", view); + baseMaterial->Set("u_CameraPosition", s_PreviewCameraPos); + baseMaterial->Set("u_IBLContribution", 0.4f); + baseMaterial->Set("u_EnvRadianceTex", s_PreviewEnvRadiance); + baseMaterial->Set("u_EnvIrradianceTex", s_PreviewEnvIrradiance); + baseMaterial->Set("u_BRDFLUTTexture", Renderer3D::GetBRDFLUT()); - // 构建简化的光照环境(一个方向光,无阴影) LightEnvironment lightEnv; lightEnv.DirectionalLights.Direction = glm::normalize(glm::vec3(-0.5f, -1.0f, -0.8f)); lightEnv.DirectionalLights.Radiance = glm::vec3(1.0f); - lightEnv.DirectionalLights.Intensity = 2.0f; + lightEnv.DirectionalLights.Intensity = 3.0f; lightEnv.DirectionalLights.CastShadows = false; lightEnv.PointLightCount = 0; lightEnv.SpotLightCount = 0; - // 构建相机矩阵 - float aspect = (float)m_PreviewRenderPass->GetSpecification().TargetFramebuffer->GetWidth() / (float)m_PreviewRenderPass->GetSpecification().TargetFramebuffer->GetHeight(); - glm::mat4 projection = glm::perspective(glm::radians(m_PreviewFOV), aspect, m_PreviewNear, m_PreviewFar); - glm::mat4 view = glm::lookAt(m_PreviewCameraPos, m_PreviewCameraTarget, glm::vec3(0,1,0)); - glm::mat4 viewProjection = projection * view; - glm::vec3 cameraPos = m_PreviewCameraPos; + baseMaterial->Set("u_DirectionalLights", lightEnv.DirectionalLights); + baseMaterial->Set("u_PointLightCount", 0); + baseMaterial->Set("u_SpotLightCount", 0); + baseMaterial->Set("u_ShadowEnabled", false); - // 准备材质参数 - auto matInst = m_PreviewMaterialInstance; - matInst->Set("u_AlbedoColor", m_Asset->AlbedoColor); - matInst->Set("u_Metalness", m_Asset->Metalness); - matInst->Set("u_Roughness", m_Asset->Roughness); - matInst->Set("u_AlbedoTexToggle", m_Asset->AlbedoTexToggle); - matInst->Set("u_NormalTexToggle", m_Asset->NormalTexToggle); - matInst->Set("u_MetalnessTexToggle", m_Asset->MetalnessTexToggle); - matInst->Set("u_RoughnessTexToggle", m_Asset->RoughnessTexToggle); + Renderer::BeginRenderPass(s_PreviewRenderPass); + Renderer::SubmitMesh(s_PreviewSphere, glm::mat4(1.0f), matInst); + Renderer::EndRenderPass(); - - if (m_Asset->AlbedoTexture) matInst->Set("u_AlbedoTexture", m_Asset->AlbedoTexture); - if (m_Asset->NormalTexture) matInst->Set("u_NormalTexture", m_Asset->NormalTexture); - if (m_Asset->MetalnessTexture) matInst->Set("u_MetalnessTexture", m_Asset->MetalnessTexture); - if (m_Asset->RoughnessTexture) matInst->Set("u_RoughnessTexture", m_Asset->RoughnessTexture); - - // 设置通用 Uniform(与 GeometryPass 类似) - matInst->Set("u_ViewProjectionMatrix", viewProjection); - matInst->Set("u_ViewMatrix", view); - matInst->Set("u_CameraPosition", cameraPos); - - // 环境光 - matInst->Set("u_EnvRadianceTex", Renderer3D::GetBlackCubeTexture()); - matInst->Set("u_EnvIrradianceTex", Renderer3D::GetBlackCubeTexture()); - matInst->Set("u_IBLContribution", 0.0f); // 关闭 IBL,仅用方向光 - - // 灯光数据 - matInst->Set("u_DirectionalLights", lightEnv.DirectionalLights); - matInst->Set("u_PointLightCount", 0); - matInst->Set("u_SpotLightCount", 0); - - // 禁用阴影 - matInst->Set("u_ShadowEnabled", false); - - // 开始渲染 - Renderer::BeginRenderPass(m_PreviewRenderPass); + if (!materialAsset->PreviewTexture) + materialAsset->PreviewTexture = Texture2D::Create(TextureFormat::RGBA16F, 512, 512); auto cmd = Renderer::GetCommandBuffer(); - auto& fb = m_PreviewRenderPass->GetSpecification().TargetFramebuffer; - - Renderer::Submit([cmd]() { - cmd->Clear(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); + auto fb = s_PreviewRenderPass->GetSpecification().TargetFramebuffer; + auto tex = materialAsset->PreviewTexture; + Renderer::Submit([cmd, fb, tex]() { + cmd->ResolveMultisampleTexture(fb, tex); }); - Renderer::SubmitMesh(m_PreviewSphere, glm::mat4(1.0f), matInst); - - if (!m_Asset->PreviewTexture) - { - m_Asset->PreviewTexture = Texture2D::Create(TextureFormat::RGBA16F, 512, 512); - } - auto& tex = m_Asset->PreviewTexture; - - - Renderer::Submit([cmd, &fb, tex]() - { - cmd->ResolveMultisampleTexture(fb,tex); - }); - - Renderer::EndRenderPass(); + materialAsset->PreviewIsDirty = false; } void PBRMaterialEditor::Render() @@ -215,27 +213,24 @@ namespace Prism { if (ImGui::BeginTabItem("Basic Edit panel")) { + if (m_Asset->PreviewTexture) + { + float previewSize = 256.0f; + ImGui::Image((ImTextureID)(intptr_t)m_Asset->PreviewTexture->GetRendererID(), + ImVec2(previewSize, previewSize), ImVec2(0, 1), ImVec2(1, 0)); + } RenderBasicEditorPanel(); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Graph Node Edit")) { - // TODO: using ImGui Node Editor impl RenderNodeEditorPanel(); ImGui::EndTabItem(); } ImGui::EndTabBar(); } - - // TODO: this function don't work - /* - if (m_Asset->IsDirty) - { - RenderPreview(); - } - */ } @@ -252,21 +247,21 @@ namespace Prism if (ImGui::ColorEdit3("Color##Albedo", glm::value_ptr(material->AlbedoColor))) { - m_Asset->IsDirty = true; + MarkDirty(); } bool useAlbedoMap = (material->AlbedoTexToggle > 0.5f); if (ImGui::Checkbox("Use Texture##Albedo", &useAlbedoMap)) { material->AlbedoTexToggle = useAlbedoMap ? 1.0f : 0.0f; - m_Asset->IsDirty = true; + MarkDirty(); } DrawTextureSlot(material->AlbedoTexture, [&](const Ref& newTex) { material->AlbedoTexture = newTex; material->AlbedoTexToggle = true; - m_Asset->IsDirty = true; + MarkDirty(); }); ImGui::SameLine(); ImGui::Text("Albedo Texture"); @@ -283,14 +278,14 @@ namespace Prism if (ImGui::Checkbox("Use Texture##Normal", &useNormalMap)) { material->NormalTexToggle = useNormalMap ? 1.0f : 0.0f; - m_Asset->IsDirty = true; + MarkDirty(); } DrawTextureSlot(material->NormalTexture, [&](const Ref& newTex) { material->NormalTexture = newTex; material->NormalTexToggle = true; - m_Asset->IsDirty = true; + MarkDirty(); }); ImGui::SameLine(); ImGui::Text("Normal Texture"); @@ -306,21 +301,21 @@ namespace Prism if (ImGui::SliderFloat("Value##Metalness", &material->Metalness, 0.0f, 1.0f)) { // 自动保存 - m_Asset->IsDirty = true; + MarkDirty(); } bool useMetalnessMap = (material->MetalnessTexToggle > 0.5f); if (ImGui::Checkbox("Use Texture##Metalness", &useMetalnessMap)) { material->MetalnessTexToggle = useMetalnessMap ? 1.0f : 0.0f; - m_Asset->IsDirty = true; + MarkDirty(); } DrawTextureSlot(material->MetalnessTexture, [&](const Ref& newTex) { material->MetalnessTexture = newTex; material->MetalnessTexToggle = true; - m_Asset->IsDirty = true; + MarkDirty(); }); ImGui::SameLine(); ImGui::Text("Metalness Texture"); @@ -336,21 +331,21 @@ namespace Prism if (ImGui::SliderFloat("Value##Roughness", &material->Roughness, 0.0f, 1.0f)) { // 自动保存 - m_Asset->IsDirty = true; + MarkDirty(); } bool useRoughnessMap = (material->RoughnessTexToggle > 0.5f); if (ImGui::Checkbox("Use Texture##Roughness", &useRoughnessMap)) { material->RoughnessTexToggle = useRoughnessMap ? 1.0f : 0.0f; - m_Asset->IsDirty = true; + MarkDirty(); } DrawTextureSlot(material->RoughnessTexture, [&](const Ref& newTex) { material->RoughnessTexture = newTex; material->RoughnessTexToggle = true; - m_Asset->IsDirty = true; + MarkDirty(); }); ImGui::SameLine(); ImGui::Text("Roughness Texture"); @@ -413,7 +408,7 @@ namespace Prism if (ImGui::Button("Compile to Asset")) { CompileMaterialToAsset(); - m_Asset->IsDirty = true; + MarkDirty(); } ed::Begin("MaterialNodeEditor"); @@ -453,7 +448,7 @@ namespace Prism void PBRMaterialEditor::DrawOutputNode() { - const float nodeWidth = 240.0f; + const float nodeWidth = 100.0f; ed::PushStyleVar(ed::StyleVar_NodePadding, ImVec4(8, 4, 8, 8)); ed::BeginNode(ed::NodeId(OUTPUT_NODE_ID)); @@ -462,8 +457,17 @@ namespace Prism ImGui::BeginGroup(); float headerMaxY = DrawNodeHeader("PBR Material Output", nodeWidth); - // TODO: this will add preview texture - ImGui::Dummy(ImVec2(nodeWidth, 128)); + if (m_Asset->PreviewTexture) + { + float previewWidth = nodeWidth; + float previewHeight = nodeWidth; + ImGui::Image((ImTextureID)(intptr_t)m_Asset->PreviewTexture->GetRendererID(), + ImVec2(previewWidth, previewHeight), ImVec2(0, 1), ImVec2(1, 0)); + } + else + { + ImGui::Dummy(ImVec2(nodeWidth, 128)); + } ImGui::Spacing(); // 输入引脚 @@ -501,14 +505,14 @@ namespace Prism { ImGui::SetNextItemWidth(80); if (ImGui::SliderFloat("##metallic", &m_Asset->Metalness, 0.0f, 1.0f)) - m_Asset->IsDirty = true; + MarkDirty(); }); drawInputPin(Roughness, "Roughness", [&]() { ImGui::SetNextItemWidth(80); if (ImGui::SliderFloat("##roughness", &m_Asset->Roughness, 0.0f, 1.0f)) - m_Asset->IsDirty = true; + MarkDirty(); }); drawInputPin(AO, "AO", [&]() @@ -582,7 +586,7 @@ namespace Prism if (AssetsManager::IsAssetType(handle, AssetType::Texture)) { textureHandle = handle; - m_Asset->IsDirty = true; + MarkDirty(); } } ImGui::EndDragDropTarget(); @@ -647,7 +651,7 @@ namespace Prism if (ed::AcceptNewItem(GetPinColor(pinType), 2.0f)) { m_Links.push_back({m_NextLinkId++, start, end}); - m_Asset->IsDirty = true; + MarkDirty(); CompileMaterialToAsset(); } } @@ -672,7 +676,7 @@ namespace Prism int id = (int)linkId.Get(); m_Links.erase(std::remove_if(m_Links.begin(), m_Links.end(), [id](const Link& l) { return l.ID == id; }), m_Links.end()); - m_Asset->IsDirty = true; + MarkDirty(); CompileMaterialToAsset(); } } @@ -692,7 +696,7 @@ namespace Prism m_TextureNodes.erase(std::remove_if(m_TextureNodes.begin(), m_TextureNodes.end(), [id](const TextureNode& n) { return n.ID == id; }), m_TextureNodes.end()); - m_Asset->IsDirty = true; + MarkDirty(); } } } @@ -728,7 +732,7 @@ namespace Prism ImVec2 canvasPos = ed::ScreenToCanvas(mousePos); int newId = m_NextNodeId++; m_TextureNodes.push_back({newId, dropHandle, canvasPos.x, canvasPos.y}); - m_Asset->IsDirty = true; + MarkDirty(); } } ImGui::EndDragDropTarget(); @@ -773,7 +777,7 @@ namespace Prism m_TextureNodes.erase(std::remove_if(m_TextureNodes.begin(), m_TextureNodes.end(), [id](const TextureNode& n) { return n.ID == id; }), m_TextureNodes.end()); - m_Asset->IsDirty = true; + MarkDirty(); CompileMaterialToAsset(); } } @@ -790,7 +794,7 @@ namespace Prism if (ImGui::BeginPopup("AlbedoColorPicker")) { if (ImGui::ColorPicker3("##picker", glm::value_ptr(m_Asset->AlbedoColor))) - m_Asset->IsDirty = true; + MarkDirty(); ImGui::EndPopup(); } @@ -841,7 +845,7 @@ namespace Prism m_Asset->RoughnessTexture = getTexture(GetLinkedTextureHandle(InputPinID(Roughness))); m_Asset->RoughnessTexToggle = m_Asset->RoughnessTexture ? 1.0f : 0.0f; - m_Asset->IsDirty = true; + MarkDirty(); } void PBRMaterialEditor::LoadAssetToGraph() diff --git a/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.h b/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.h index 8faae20..6b4ccdb 100644 --- a/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.h +++ b/Prism/src/Prism/Editor/DefaultAssetEditors/PBRMaterialAssetEditor.h @@ -25,29 +25,18 @@ namespace Prism Ref GetAsset() override { return m_Asset; } void SetAsset(const Ref& asset) override; + static void InitPreviewRenderer(); + static void RenderMaterialPreview(Ref materialAsset); + static Ref GetEditingAsset() { return s_EditingAsset; } + private: Ref m_Asset; ed::EditorContext* m_EditorContext = nullptr; + static Ref s_EditingAsset; - // 预览贴图 - Ref m_PreviewRenderPass; - Ref m_PreviewSphere; - Ref m_PreviewMaterialInstance; - - // camera 相关 - glm::vec3 m_PreviewCameraPos = glm::vec3(3.0f, 2.0f, 5.0f); - glm::vec3 m_PreviewCameraTarget = glm::vec3(0.0f); - float m_PreviewFOV = 45.0f; - float m_PreviewNear = 0.1f; - float m_PreviewFar = 100.0f; - - void InitPreviewResources(); - void RenderPreview(); - - - // 原有渲染函数 void Render() override; void RenderBasicEditorPanel(); + void MarkDirty() { m_Asset->IsDirty = true; m_Asset->PreviewIsDirty = true; } // ---------- 节点编辑器相关 ---------- void RenderNodeEditorPanel(); // 新的节点编辑器面板 diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.cpp index 7021ede..004e530 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.cpp @@ -405,6 +405,21 @@ namespace Prism glUseProgram(programID); } + void OpenGLCommandBuffer::BindVertexArray(const uint32_t vao) const + { + glBindVertexArray(vao); + } + + void OpenGLCommandBuffer::BindVertexBuffer(const uint32_t vbo) const + { + glBindBuffer(GL_ARRAY_BUFFER, vbo); + } + + void OpenGLCommandBuffer::BindIndexBuffer(const uint32_t ibo) const + { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo); + } + void OpenGLCommandBuffer::BindStorageBuffer(const uint32_t binding, const Ref& ref) const { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, binding, ref->GetRendererID()); @@ -426,6 +441,37 @@ namespace Prism glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); } + void OpenGLCommandBuffer::ResolveMultisampleTexture(const Ref& src, uint32_t attachmentIndex, const Ref& dst) const + { + uint32_t resolveFBO; + glGenFramebuffers(1, &resolveFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst->GetRendererID(), 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, src->GetRendererID()); + glReadBuffer(GL_COLOR_ATTACHMENT0 + attachmentIndex); + glBlitFramebuffer(0, 0, src->GetWidth(), src->GetHeight(), + 0, 0, src->GetWidth(), src->GetHeight(), + GL_COLOR_BUFFER_BIT, GL_LINEAR); + glDeleteFramebuffers(1, &resolveFBO); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } + + void OpenGLCommandBuffer::ResolveMultisampleDepth(const Ref& src, const Ref& dst) const + { + uint32_t resolveFBO; + glGenFramebuffers(1, &resolveFBO); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO); + glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, dst->GetRendererID(), 0); + glBindFramebuffer(GL_READ_FRAMEBUFFER, src->GetRendererID()); + glBlitFramebuffer(0, 0, src->GetWidth(), src->GetHeight(), + 0, 0, src->GetWidth(), src->GetHeight(), + GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT, GL_NEAREST); + glDeleteFramebuffers(1, &resolveFBO); + glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + } + void OpenGLCommandBuffer::CopyFrameToTexture(const Ref& src, const Ref& dst) const { uint32_t tempFBO; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.h b/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.h index 8289849..717766d 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLCommandBuffer.h @@ -85,10 +85,15 @@ namespace Prism void ProgramUniform1f(uint32_t program, int32_t location, float value) const override; void UseProgram(uint32_t programID) const override; + void BindVertexArray(uint32_t vao) const override; + void BindVertexBuffer(uint32_t vbo) const override; + void BindIndexBuffer(uint32_t ibo) const override; void BindStorageBuffer(uint32_t binding, const Ref& ref) const override; void ResolveMultisampleTexture(const Ref& src, const Ref& dst) const override; + void ResolveMultisampleTexture(const Ref& src, uint32_t attachmentIndex, const Ref& dst) const override; + void ResolveMultisampleDepth(const Ref& src, const Ref& dst) const override; void CopyFrameToTexture(const Ref& src, const Ref& dst) const override; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLPipeline.h b/Prism/src/Prism/Platform/OpenGL/OpenGLPipeline.h index dacfd75..dbf4eb6 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLPipeline.h +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLPipeline.h @@ -20,6 +20,7 @@ namespace Prism { virtual void Invalidate() override; virtual void Bind() override; + virtual RendererID GetVertexArrayRendererID() const override { return m_VertexArrayRendererID; } private: PipelineSpecification m_Specification; uint32_t m_VertexArrayRendererID = 0; diff --git a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp index ac509d4..53d7e26 100644 --- a/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp +++ b/Prism/src/Prism/Platform/OpenGL/OpenGLTexture.cpp @@ -20,6 +20,8 @@ namespace Prism case TextureFormat::RGBA16F: return GL_RGBA16F; case TextureFormat::RG8: return GL_RG8; case TextureFormat::RG16F: return GL_RG16F; + case TextureFormat::DEPTH24STENCIL8: return GL_DEPTH24_STENCIL8; + case TextureFormat::DEPTH32F: return GL_DEPTH_COMPONENT32F; } return 0; } @@ -31,6 +33,8 @@ namespace Prism case TextureFormat::RGBA16F: return GL_RGBA16F; case TextureFormat::RG8: return GL_RG8; case TextureFormat::RG16F: return GL_RG16F; + case TextureFormat::DEPTH24STENCIL8: return GL_DEPTH24_STENCIL8; + case TextureFormat::DEPTH32F: return GL_DEPTH_COMPONENT32F; default: return 0; } } @@ -42,6 +46,8 @@ namespace Prism case TextureFormat::RGBA16F: return GL_RGBA; case TextureFormat::RG8: return GL_RG; case TextureFormat::RG16F: return GL_RG; + case TextureFormat::DEPTH24STENCIL8: return GL_DEPTH_STENCIL; + case TextureFormat::DEPTH32F: return GL_DEPTH_COMPONENT; default: return 0; } } @@ -51,6 +57,8 @@ namespace Prism case TextureFormat::RGB: case TextureFormat::RGBA: return GL_UNSIGNED_BYTE; case TextureFormat::RGBA16F: return GL_HALF_FLOAT; + case TextureFormat::DEPTH24STENCIL8: return GL_UNSIGNED_INT_24_8; + case TextureFormat::DEPTH32F: return GL_FLOAT; default: return 0; } } diff --git a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp index e2385cf..d8e2afc 100644 --- a/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp +++ b/Prism/src/Prism/Platform/Windows/WindowsFileSystemWatcher.cpp @@ -1,7 +1,3 @@ -// -// Created by Atdunbg on 2026/1/21. -// - #include #include "Prism/Utilities/FileSystem.h" @@ -22,7 +18,12 @@ namespace Prism { FileSystem::FileSystemChangedCallbackFn FileSystem::s_Callback; - std::atomic FileSystem::s_IgnoreNextChange = false; + std::mutex FileSystem::s_EventQueueMutex; + std::vector FileSystem::s_PendingEvents; + + std::mutex FileSystem::s_RecentWritesMutex; + std::unordered_map FileSystem::s_RecentWrites; + efsw::FileWatcher* FileSystem::s_FileWatcher = nullptr; PrismFileWatcherListener* FileSystem::s_Listener = nullptr; @@ -35,10 +36,6 @@ namespace Prism efsw::Action action, std::string oldFilepath = "") override { - // 如果引擎自身操作设置了忽略标志,则跳过本次事件 - if (FileSystem::s_IgnoreNextChange.load()) - return; - std::string fullPath = (std::filesystem::path(dir) / filepath).string(); fullPath = Utils::NormalizePath(fullPath); @@ -46,16 +43,13 @@ namespace Prism FileSystemChangedEvent e; e.FilePath = fullPath; e.NewName = Utils::GetFilename(filepath); - e.OldName = Utils::GetFilename(oldFilepath); // efsw 在重命名时会提供旧文件名 - e.IsDirectory = false; // 稍后根据实际情况判断 + e.OldName = Utils::GetFilename(oldFilepath); + e.IsDirectory = false; - // 判断是否为目录(可能抛出异常,使用 error_code 版本) std::error_code ec; if (std::filesystem::is_directory(fullPath, ec)) e.IsDirectory = true; - // 如果 ec 有错误(文件可能已被删除),保持 false - // 映射 efsw 动作到你的枚举 switch (action) { case efsw::Actions::Add: @@ -71,12 +65,13 @@ namespace Prism e.Action = FileSystemAction::Rename; break; default: - return; // 忽略未知事件 + return; } - // 调用外部回调(需确保线程安全) - if (FileSystem::s_Callback) - FileSystem::s_Callback(e); + { + std::lock_guard lock(FileSystem::s_EventQueueMutex); + FileSystem::s_PendingEvents.push_back(e); + } } }; @@ -96,7 +91,6 @@ namespace Prism } } - // 构建描述文字 if (!patterns.empty()) { description = "Custom files ("; for (size_t i = 0; i < patterns.size(); ++i) { @@ -105,7 +99,6 @@ namespace Prism } description += ")"; - // 准备 C 字符串数组 for (const auto& p : patterns) { patternCstrs.push_back(p.c_str()); } @@ -113,12 +106,12 @@ namespace Prism } const char* result = tinyfd_openFileDialog( - "Open File", // title - "", // default open path - static_cast(patternCstrs.size()), // filter count - patternCstrs.empty() ? nullptr : patternCstrs.data(), // filter pattern - description.c_str(), // filter description - 0 // 0 is single select, 1 is Multiple select + "Open File", + "", + static_cast(patternCstrs.size()), + patternCstrs.empty() ? nullptr : patternCstrs.data(), + description.c_str(), + 0 ); return result ? std::string(result) : std::string(); @@ -176,13 +169,11 @@ namespace Prism std::string cmd; if (std::filesystem::is_directory(absPath)) { - // 打开目录 cmd = absPath.string(); ShellExecuteA(nullptr, "open", "explorer.exe", cmd.c_str(), NULL, SW_SHOW); } else { - // 打开并选中文件 cmd = "/select, \"" + absPath.string() + "\""; ShellExecuteA(nullptr, "open", "explorer.exe", cmd.c_str(), NULL, SW_SHOW); } @@ -225,21 +216,20 @@ namespace Prism std::string FileSystem::Rename(const std::string& filepath, const std::string& newName) { - s_IgnoreNextChange = true; const std::filesystem::path p = filepath; std::string newFilePath = p.parent_path().string() + "/" + newName + p.extension().string(); + NotifyFileWriting(filepath); + NotifyFileWriting(newFilePath); MoveFileA(filepath.c_str(), newFilePath.c_str()); - s_IgnoreNextChange = false; return newFilePath; } bool FileSystem::PrismDeleteFile(const std::string& filepath) { - s_IgnoreNextChange = true; + NotifyFileWriting(filepath); std::error_code ec; const bool removed = std::filesystem::remove(filepath, ec); - s_IgnoreNextChange = false; if (!removed || ec) { PM_CORE_ERROR("Delete failed: {}", ec.message()); @@ -250,14 +240,56 @@ namespace Prism bool FileSystem::PrismMoveFile(const std::string& filepath, const std::string& dest) { - s_IgnoreNextChange = true; + NotifyFileWriting(filepath); std::filesystem::path p = filepath; const std::string destFilePath = dest + "/" + p.filename().string(); + NotifyFileWriting(destFilePath); const BOOL result = MoveFileA(filepath.c_str(), destFilePath.c_str()); - s_IgnoreNextChange = false; return result != 0; } + void FileSystem::ProcessPendingEvents() + { + std::vector events; + { + std::lock_guard lock(s_EventQueueMutex); + events.swap(s_PendingEvents); + } + + auto now = std::chrono::steady_clock::now(); + { + std::lock_guard lock(s_RecentWritesMutex); + for (auto it = s_RecentWrites.begin(); it != s_RecentWrites.end(); ) + { + auto elapsed = std::chrono::duration_cast(now - it->second).count(); + if (elapsed > 2000) + it = s_RecentWrites.erase(it); + else + ++it; + } + } + + for (const auto& e : events) + { + if (e.Action == FileSystemAction::Modified) + { + std::lock_guard lock(s_RecentWritesMutex); + if (s_RecentWrites.find(e.FilePath) != s_RecentWrites.end()) + continue; + } + + if (s_Callback) + s_Callback(e); + } + } + + void FileSystem::NotifyFileWriting(const std::string& filepath) + { + std::string normalized = Utils::NormalizePath(filepath); + std::lock_guard lock(s_RecentWritesMutex); + s_RecentWrites[normalized] = std::chrono::steady_clock::now(); + } + void FileSystem::StartWatching() { diff --git a/Prism/src/Prism/Renderer/Mesh.h b/Prism/src/Prism/Renderer/Mesh.h index cb5d2f2..8af468b 100644 --- a/Prism/src/Prism/Renderer/Mesh.h +++ b/Prism/src/Prism/Renderer/Mesh.h @@ -144,6 +144,13 @@ namespace Prism const AABB& GetBoundingBox() const { return m_BoundingBox; } + void Bind() + { + m_VertexBuffer->Bind(); + m_Pipeline->Bind(); + m_IndexBuffer->Bind(); + } + private: void TraverseNodes(const aiNode* node, const glm::mat4& parentTransform = glm::mat4(1.0f), uint32_t level = 0); diff --git a/Prism/src/Prism/Renderer/MeshFactory.cpp b/Prism/src/Prism/Renderer/MeshFactory.cpp index c164db4..fe2bf32 100644 --- a/Prism/src/Prism/Renderer/MeshFactory.cpp +++ b/Prism/src/Prism/Renderer/MeshFactory.cpp @@ -56,13 +56,13 @@ namespace Prism constexpr uint32_t latitudeBands = 30; constexpr float longitudeBands = 30; - for (float latitude = 0.0f; latitude <= latitudeBands; latitude++) + for (uint32_t latitude = 0; latitude <= latitudeBands; latitude++) { const float theta = latitude * M_PI / latitudeBands; const float sinTheta = glm::sin(theta); float cosTheta = glm::cos(theta); - for (float longitude = 0.0f; longitude <= longitudeBands; longitude++) + for (uint32_t longitude = 0; longitude <= static_cast(longitudeBands); longitude++) { const float phi = longitude * 2 * M_PI / longitudeBands; const float sinPhi = glm::sin(phi); @@ -71,6 +71,7 @@ namespace Prism Vertex vertex; vertex.Normal = { cosPhi * sinTheta, cosTheta, sinPhi * sinTheta }; vertex.Position = { radius * vertex.Normal.x, radius * vertex.Normal.y, radius * vertex.Normal.z }; + vertex.Texcoord = { (float)longitude / (float)longitudeBands, (float)latitude / (float)latitudeBands }; vertices.push_back(vertex); } } diff --git a/Prism/src/Prism/Renderer/Pipeline.h b/Prism/src/Prism/Renderer/Pipeline.h index d9b3fdb..d464a26 100644 --- a/Prism/src/Prism/Renderer/Pipeline.h +++ b/Prism/src/Prism/Renderer/Pipeline.h @@ -25,8 +25,8 @@ namespace Prism { virtual void Invalidate() = 0; - // TEMP: remove this when render command buffers are a thing virtual void Bind() = 0; + virtual RendererID GetVertexArrayRendererID() const = 0; static Ref Create(const PipelineSpecification& spec); }; diff --git a/Prism/src/Prism/Renderer/RHI/RHICommandBuffer.h b/Prism/src/Prism/Renderer/RHI/RHICommandBuffer.h index beaf454..867bdc9 100644 --- a/Prism/src/Prism/Renderer/RHI/RHICommandBuffer.h +++ b/Prism/src/Prism/Renderer/RHI/RHICommandBuffer.h @@ -113,9 +113,14 @@ namespace Prism { virtual int32_t GetUniformLocation(uint32_t program, const std::string& name) const = 0; virtual void UseProgram(uint32_t programID) const = 0; + virtual void BindVertexArray(uint32_t vao) const = 0; + virtual void BindVertexBuffer(uint32_t vbo) const = 0; + virtual void BindIndexBuffer(uint32_t ibo) const = 0; virtual void BindStorageBuffer(uint32_t binding, const Ref& ref) const = 0; virtual void ResolveMultisampleTexture(const Ref& src, const Ref& dst) const = 0; + virtual void ResolveMultisampleTexture(const Ref& src, uint32_t attachmentIndex, const Ref& dst) const = 0; + virtual void ResolveMultisampleDepth(const Ref& src, const Ref& dst) const = 0; virtual void CopyFrameToTexture(const Ref& src, const Ref& dst) const = 0; diff --git a/Prism/src/Prism/Renderer/Renderer3D.cpp b/Prism/src/Prism/Renderer/Renderer3D.cpp index 7b7c091..8a4c506 100644 --- a/Prism/src/Prism/Renderer/Renderer3D.cpp +++ b/Prism/src/Prism/Renderer/Renderer3D.cpp @@ -7,6 +7,9 @@ #include "Prism/Core/Timer.h" #include "Prism/Scene/Scene.h" +#include +#include + #include "Renderer.h" #include "Renderer2D.h" #include "SceneRenderer.h" @@ -19,6 +22,8 @@ namespace Prism { struct Renderer3DData { + static constexpr int CSM_CASCADE_COUNT = 4; + struct SceneInfo { SceneRendererCamera SceneCamera; @@ -42,16 +47,20 @@ namespace Prism struct ShadowData { Ref ShadowMapShader, ShadowMapAnimShader; - Ref ShadowMapRenderPass; + Ref ShadowMapRenderPass[CSM_CASCADE_COUNT]; bool ShadowEnabled = true; float ShadowBias = 0.001f; float ShadowIntensity = 1.0f; int ShadowSoftness = 1; - // Dynamic shadow mapping - glm::vec3 SceneCenter{ 0.0f }; - glm::vec3 SceneBounds{ 50.0f }; // Default bounds + float CascadeSplitLambda = 0.95f; + float ShadowFarOverride = 200.0f; + float ShadowFar = 200.0f; + float ShadowDepthExtend = 50.0f; + float CascadeSplits[CSM_CASCADE_COUNT]; + glm::mat4 LightSpaceMatrices[CSM_CASCADE_COUNT]; + float OrthoSize = 100.0f; }ShadowData; @@ -93,7 +102,7 @@ namespace Prism RendererID ShadowMapSampler; - glm::mat4 LightMatrices; + glm::mat4 LightMatrices[CSM_CASCADE_COUNT]; bool EnableBloom = false; float BloomThreshold = 1.5f; @@ -130,6 +139,23 @@ namespace Prism float FadeDistance = 500.0f; }GridData; + struct SSRData + { + bool EnableSSR = false; + float Intensity = 1.0f; + float MaxDistance = 30.0f; + float Thickness = 0.5f; + int Steps = 64; + float BlurRadius = 4.0f; + + Ref SSRShader; + Ref SSRBlurShader; + Ref SSRRenderPass; + Ref SSRBlurRenderPass; + Ref SSRDepthTexture; + Ref SSRMaterialInfoTexture; + } SSRData; + SceneRendererOptions Options; Ref BlackCubeTexture; @@ -159,7 +185,7 @@ namespace Prism //////////////// GeoPass //////////////// { FramebufferSpecification geoFramebufferSpec; - geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA32F, FramebufferTextureFormat::DEPTH24STENCIL8 }; + geoFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA32F, FramebufferTextureFormat::RGBA8, FramebufferTextureFormat::DEPTH24STENCIL8 }; geoFramebufferSpec.Samples = 8; geoFramebufferSpec.ClearColor = { 0.1f, 0.1f, 0.1f, 1.0f }; @@ -226,9 +252,12 @@ namespace Prism shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; shadowMapFramebufferSpec.NoResize = true; - RenderPassSpecification shadowMapRenderPassSpec; - shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); - s_Data.ShadowData.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec); + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) + { + RenderPassSpecification shadowMapRenderPassSpec; + shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec); + s_Data.ShadowData.ShadowMapRenderPass[i] = RenderPass::Create(shadowMapRenderPassSpec); + } auto cmd = Renderer::GetCommandBuffer(); @@ -237,6 +266,7 @@ namespace Prism desc.MagFilter = FilterMode::Linear; desc.WrapU = WrapMode::ClampToEdge; desc.WrapV = WrapMode::ClampToEdge; + desc.BorderColor = { 1.0f, 1.0f, 1.0f, 1.0f }; s_Data.ShadowMapSampler = Renderer::GetDevice()->CreateSampler(desc); } ///////////////////////////////////////////// @@ -244,6 +274,29 @@ namespace Prism s_Data.CompositeShader = Shader::Create("assets/shaders/SceneComposite.glsl"); s_Data.BRDFLUT = Texture2D::Create("assets/textures/BRDF_LUT.tga", false, true); + //////////////// SSR //////////////// + { + FramebufferSpecification ssrFramebufferSpec; + ssrFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; + ssrFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + + RenderPassSpecification ssrRenderPassSpec; + ssrRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(ssrFramebufferSpec); + s_Data.SSRData.SSRRenderPass = RenderPass::Create(ssrRenderPassSpec); + + FramebufferSpecification ssrBlurFramebufferSpec; + ssrBlurFramebufferSpec.Attachments = { FramebufferTextureFormat::RGBA16F }; + ssrBlurFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f }; + + RenderPassSpecification ssrBlurRenderPassSpec; + ssrBlurRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(ssrBlurFramebufferSpec); + s_Data.SSRData.SSRBlurRenderPass = RenderPass::Create(ssrBlurRenderPassSpec); + + s_Data.SSRData.SSRShader = Shader::Create("assets/shaders/SSR.glsl"); + s_Data.SSRData.SSRBlurShader = Shader::Create("assets/shaders/SSRBlur.glsl"); + } + ///////////////////////////////////////////// + // Grid // const auto gridShader = Shader::Create("assets/shaders/Grid.glsl"); const auto gridShader = Shader::Create("assets/shaders/InfiniteGrid.glsl"); @@ -284,6 +337,11 @@ namespace Prism s_Data.BloomBlurPass[0]->GetSpecification().TargetFramebuffer->Resize(width, height); s_Data.BloomBlurPass[1]->GetSpecification().TargetFramebuffer->Resize(width, height); s_Data.BloomBlendPass->GetSpecification().TargetFramebuffer->Resize(width, height); + + if (s_Data.SSRData.SSRRenderPass) + s_Data.SSRData.SSRRenderPass->GetSpecification().TargetFramebuffer->Resize(width, height); + if (s_Data.SSRData.SSRBlurRenderPass) + s_Data.SSRData.SSRBlurRenderPass->GetSpecification().TargetFramebuffer->Resize(width, height); } void Renderer3D::BeginScene(const Scene* scene, const SceneRendererCamera& camera) @@ -468,6 +526,10 @@ namespace Prism return s_Data.BlackTexture; } + Ref Renderer3D::GetBRDFLUT() + { + return s_Data.BRDFLUT; + } void Renderer3D::FlushDrawList(Ref& outRenderPass) { @@ -493,6 +555,12 @@ namespace Prism ResolveMSAA(); + if (s_Data.SSRData.EnableSSR) + { + SSRPass(); + SSRBlurPass(); + } + BloomBlurPass(); GridPass(); @@ -589,57 +657,121 @@ namespace Prism if (!s_Data.ShadowData.ShadowEnabled || directionalLights.Intensity == 0.0f || !directionalLights.CastShadows) { - Renderer::BeginRenderPass(s_Data.ShadowData.ShadowMapRenderPass); - Renderer::EndRenderPass(); + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) + { + Renderer::BeginRenderPass(s_Data.ShadowData.ShadowMapRenderPass[i]); + Renderer::EndRenderPass(); + } return; } - const float shadowOrthoSize = s_Data.ShadowData.OrthoSize; - const float shadowNear = 0.1f; - const float shadowFar = shadowOrthoSize * 6.0f; + const auto& sceneCamera = s_Data.SceneData.SceneCamera; + const float cameraNear = sceneCamera.Camera.GetNear(); + const float cameraFar = sceneCamera.Camera.GetFar(); + const float shadowFar = glm::min(s_Data.ShadowData.ShadowFarOverride, cameraFar); + s_Data.ShadowData.ShadowFar = shadowFar; + const float lambda = s_Data.ShadowData.CascadeSplitLambda; + const glm::mat4 cameraProj = sceneCamera.Camera.GetProjectionMatrix(); + const glm::mat4 cameraView = sceneCamera.ViewMatrix; + const glm::mat4 cameraInvViewProj = glm::inverse(cameraProj * cameraView); const glm::vec3 lightDir = glm::normalize(directionalLights.Direction); - const glm::vec3 cameraPos = glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)[3]; - glm::vec3 shadowCenter = cameraPos; - shadowCenter.y = 0.0f; - glm::vec3 lightUp = glm::vec3(0.0f, 1.0f, 0.0f); if (glm::abs(lightDir.y) > 0.9f) lightUp = glm::vec3(1.0f, 0.0f, 0.0f); - const glm::vec3 lightPos = shadowCenter + lightDir * (shadowOrthoSize * 3.0f); - const glm::mat4 lightView = glm::lookAt(lightPos, shadowCenter, lightUp); - const glm::mat4 lightProj = glm::ortho( - -shadowOrthoSize, shadowOrthoSize, - -shadowOrthoSize, shadowOrthoSize, - shadowNear, shadowFar - ); - - const glm::mat4 lightSpaceMatrix = lightProj * lightView; - s_Data.LightMatrices = lightSpaceMatrix; - - - Renderer::Submit([cmd]() + glm::vec4 fullFrustumCorners[8]; + glm::vec4 ndcCorners[8] = { + {-1.0f, -1.0f, -1.0f, 1.0f}, {1.0f, -1.0f, -1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f, 1.0f}, {-1.0f, 1.0f, -1.0f, 1.0f}, + {-1.0f, -1.0f, 1.0f, 1.0f}, {1.0f, -1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, 1.0f, 1.0f}, {-1.0f, 1.0f, 1.0f, 1.0f} + }; + for (int i = 0; i < 8; i++) { - cmd->SetCullMode(CullMode::Back); - }); - - Renderer::BeginRenderPass(s_Data.ShadowData.ShadowMapRenderPass); - - for (auto& dc : s_Data.ShadowPassDrawList) - { - if (!dc.mesh) continue; - - Ref shader = dc.mesh->IsAnimated() - ? s_Data.ShadowData.ShadowMapAnimShader - : s_Data.ShadowData.ShadowMapShader; - - shader->SetMat4("u_LightViewProjection", lightSpaceMatrix); - Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader); + fullFrustumCorners[i] = cameraInvViewProj * ndcCorners[i]; + fullFrustumCorners[i] /= fullFrustumCorners[i].w; } - Renderer::EndRenderPass(); + for (int cascade = 0; cascade < Renderer3DData::CSM_CASCADE_COUNT; cascade++) + { + float splitDist = float(cascade + 1) / float(Renderer3DData::CSM_CASCADE_COUNT); + float logSplit = cameraNear * std::pow(shadowFar / cameraNear, splitDist); + float linearSplit = cameraNear + (shadowFar - cameraNear) * splitDist; + float splitZ = lambda * logSplit + (1.0f - lambda) * linearSplit; + + s_Data.ShadowData.CascadeSplits[cascade] = (splitZ - cameraNear) / (shadowFar - cameraNear); + + float prevSplitZ = (cascade == 0) ? cameraNear : + cameraNear + (shadowFar - cameraNear) * s_Data.ShadowData.CascadeSplits[cascade - 1]; + float nextSplitZ = splitZ; + + float prevRatio = (cameraFar == cameraNear) ? 0.0f : + (prevSplitZ - cameraNear) / (cameraFar - cameraNear); + float nextRatio = (cameraFar == cameraNear) ? 0.0f : + (nextSplitZ - cameraNear) / (cameraFar - cameraNear); + + glm::vec4 frustumCorners[8]; + for (int i = 0; i < 4; i++) + { + glm::vec3 ray = glm::vec3(fullFrustumCorners[i + 4]) - glm::vec3(fullFrustumCorners[i]); + frustumCorners[i] = glm::vec4(glm::vec3(fullFrustumCorners[i]) + ray * prevRatio, 1.0f); + frustumCorners[i + 4] = glm::vec4(glm::vec3(fullFrustumCorners[i]) + ray * nextRatio, 1.0f); + } + + glm::vec3 center = glm::vec3(0.0f); + for (int i = 0; i < 8; i++) + center += glm::vec3(frustumCorners[i]); + center /= 8.0f; + + glm::mat4 lightView = glm::lookAt(center + lightDir, center, lightUp); + + float minX = std::numeric_limits::max(); + float maxX = std::numeric_limits::lowest(); + float minY = std::numeric_limits::max(); + float maxY = std::numeric_limits::lowest(); + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + + for (int i = 0; i < 8; i++) + { + glm::vec4 lsCorner = lightView * frustumCorners[i]; + minX = glm::min(minX, lsCorner.x); + maxX = glm::max(maxX, lsCorner.x); + minY = glm::min(minY, lsCorner.y); + maxY = glm::max(maxY, lsCorner.y); + minZ = glm::min(minZ, lsCorner.z); + maxZ = glm::max(maxZ, lsCorner.z); + } + + glm::mat4 lightProj = glm::ortho(minX, maxX, minY, maxY, -maxZ - s_Data.ShadowData.ShadowDepthExtend, -minZ); + + glm::mat4 lightSpaceMatrix = lightProj * lightView; + s_Data.ShadowData.LightSpaceMatrices[cascade] = lightSpaceMatrix; + s_Data.LightMatrices[cascade] = lightSpaceMatrix; + + Renderer::Submit([cmd]() + { + cmd->SetCullMode(CullMode::Back); + }); + + Renderer::BeginRenderPass(s_Data.ShadowData.ShadowMapRenderPass[cascade]); + + for (auto& dc : s_Data.ShadowPassDrawList) + { + if (!dc.mesh) continue; + + Ref shader = dc.mesh->IsAnimated() + ? s_Data.ShadowData.ShadowMapAnimShader + : s_Data.ShadowData.ShadowMapShader; + + shader->SetMat4("u_LightViewProjection", lightSpaceMatrix); + Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader); + } + + Renderer::EndRenderPass(); + } } @@ -743,10 +875,22 @@ namespace Prism // baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight); // shadow - baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices); baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowData.ShadowEnabled); if (s_Data.ShadowData.ShadowEnabled) { + baseMaterial->Set("u_LightSpaceMatrix0", s_Data.LightMatrices[0]); + baseMaterial->Set("u_LightSpaceMatrix1", s_Data.LightMatrices[1]); + baseMaterial->Set("u_LightSpaceMatrix2", s_Data.LightMatrices[2]); + baseMaterial->Set("u_LightSpaceMatrix3", s_Data.LightMatrices[3]); + baseMaterial->Set("u_ShadowFar", s_Data.ShadowData.ShadowFar); + baseMaterial->Set("u_ShadowNear", sceneCamera.Camera.GetNear()); + glm::vec4 cascadeSplits( + s_Data.ShadowData.CascadeSplits[0], + s_Data.ShadowData.CascadeSplits[1], + s_Data.ShadowData.CascadeSplits[2], + s_Data.ShadowData.CascadeSplits[3] + ); + baseMaterial->Set("u_CascadeSplits", cascadeSplits); baseMaterial->Set("u_ShadowBias", s_Data.ShadowData.ShadowBias); baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowData.ShadowIntensity); baseMaterial->Set("u_ShadowSoftness", (float)s_Data.ShadowData.ShadowSoftness); @@ -754,15 +898,18 @@ namespace Prism auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap"); if (rd) { - auto reg = rd->GetRegister(); - auto tex = s_Data.ShadowData.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); - - Renderer::Submit([reg, tex, cmd]() mutable + uint32_t baseReg = rd->GetRegister(); + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) { - cmd->BindTextureUnit(reg, tex); - cmd->BindSampler(reg, s_Data.ShadowMapSampler); - }); - } + uint32_t reg = baseReg + i; + auto tex = s_Data.ShadowData.ShadowMapRenderPass[i]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + Renderer::Submit([reg, tex, cmd]() mutable + { + cmd->BindTextureUnit(reg, tex); + cmd->BindSampler(reg, s_Data.ShadowMapSampler); + }); + } + } } Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.MaterialInstances); @@ -832,23 +979,37 @@ namespace Prism baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowData.ShadowEnabled); if (s_Data.ShadowData.ShadowEnabled) { - baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices); + baseMaterial->Set("u_LightSpaceMatrix0", s_Data.LightMatrices[0]); + baseMaterial->Set("u_LightSpaceMatrix1", s_Data.LightMatrices[1]); + baseMaterial->Set("u_LightSpaceMatrix2", s_Data.LightMatrices[2]); + baseMaterial->Set("u_LightSpaceMatrix3", s_Data.LightMatrices[3]); + baseMaterial->Set("u_ShadowFar", s_Data.ShadowData.ShadowFar); + baseMaterial->Set("u_ShadowNear", sceneCamera.Camera.GetNear()); + glm::vec4 cascadeSplits( + s_Data.ShadowData.CascadeSplits[0], + s_Data.ShadowData.CascadeSplits[1], + s_Data.ShadowData.CascadeSplits[2], + s_Data.ShadowData.CascadeSplits[3] + ); + baseMaterial->Set("u_CascadeSplits", cascadeSplits); baseMaterial->Set("u_ShadowBias", s_Data.ShadowData.ShadowBias); baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowData.ShadowIntensity); baseMaterial->Set("u_ShadowSoftness", (float)s_Data.ShadowData.ShadowSoftness); - auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap"); if (rd) { - auto reg = rd->GetRegister(); - auto tex = s_Data.ShadowData.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); - - Renderer::Submit([reg, tex, cmd]() mutable + uint32_t baseReg = rd->GetRegister(); + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) { - cmd->BindTextureUnit(reg, tex); - cmd->BindSampler(reg, s_Data.ShadowMapSampler); - }); + uint32_t reg = baseReg + i; + auto tex = s_Data.ShadowData.ShadowMapRenderPass[i]->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID(); + Renderer::Submit([reg, tex, cmd]() mutable + { + cmd->BindTextureUnit(reg, tex); + cmd->BindSampler(reg, s_Data.ShadowMapSampler); + }); + } } } @@ -1070,6 +1231,7 @@ namespace Prism s_Data.CompositeShader->SetFloat("u_ManualExposure", s_Data.SceneData.SceneCamera.Camera.GetExposure()); s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples); s_Data.CompositeShader->SetBool("u_EnableBloom", s_Data.EnableBloom); + s_Data.CompositeShader->SetBool("u_EnableSSR", s_Data.SSRData.EnableSSR); // Fog @@ -1093,6 +1255,12 @@ namespace Prism s_Data.CompositeShader->SetInt("u_BloomTexture", 2); } + if (s_Data.SSRData.EnableSSR) + { + s_Data.SSRData.SSRBlurRenderPass->GetSpecification().TargetFramebuffer->BindTexture(0, 3); + s_Data.CompositeShader->SetInt("u_SSRTexture", 3); + } + Renderer::SubmitFullscreenQuad(false); auto depthFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; @@ -1110,6 +1278,85 @@ namespace Prism + void Renderer3D::SSRPass() + { + auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; + const uint32_t width = srcFB->GetWidth(); + const uint32_t height = srcFB->GetHeight(); + + if (!s_Data.SSRData.SSRDepthTexture || + s_Data.SSRData.SSRDepthTexture->GetWidth() != width || + s_Data.SSRData.SSRDepthTexture->GetHeight() != height) + { + s_Data.SSRData.SSRDepthTexture = Texture2D::Create(TextureFormat::DEPTH24STENCIL8, width, height); + } + + if (!s_Data.SSRData.SSRMaterialInfoTexture || + s_Data.SSRData.SSRMaterialInfoTexture->GetWidth() != width || + s_Data.SSRData.SSRMaterialInfoTexture->GetHeight() != height) + { + s_Data.SSRData.SSRMaterialInfoTexture = Texture2D::Create(TextureFormat::RGBA, width, height); + } + + auto cmd = Renderer::GetCommandBuffer(); + + Renderer::Submit([srcFB, depthTex = s_Data.SSRData.SSRDepthTexture, cmd]() { + cmd->ResolveMultisampleDepth(srcFB, depthTex); + }); + + Renderer::Submit([srcFB, matTex = s_Data.SSRData.SSRMaterialInfoTexture, cmd]() { + cmd->ResolveMultisampleTexture(srcFB, 1, matTex); + }); + + Renderer::BeginRenderPass(s_Data.SSRData.SSRRenderPass); + + s_Data.SSRData.SSRShader->Bind(); + s_Data.SSRData.SSRShader->SetInt("u_ColorTexture", 0); + s_Data.SSRData.SSRShader->SetInt("u_DepthTexture", 1); + s_Data.SSRData.SSRShader->SetInt("u_MaterialInfoTexture", 2); + + s_Data.ResolvedHDRTexture->Bind(0); + s_Data.SSRData.SSRDepthTexture->Bind(1); + s_Data.SSRData.SSRMaterialInfoTexture->Bind(2); + + const auto& sceneCamera = s_Data.SceneData.SceneCamera; + const auto cameraProjection = sceneCamera.Camera.GetProjectionMatrix(); + + s_Data.SSRData.SSRShader->SetMat4("u_Projection", cameraProjection); + s_Data.SSRData.SSRShader->SetMat4("u_InvProjection", glm::inverse(cameraProjection)); + s_Data.SSRData.SSRShader->SetFloat2("u_ScreenSize", glm::vec2((float)width, (float)height)); + s_Data.SSRData.SSRShader->SetFloat("u_CameraNear", sceneCamera.Camera.GetNear()); + s_Data.SSRData.SSRShader->SetFloat("u_CameraFar", sceneCamera.Camera.GetFar()); + s_Data.SSRData.SSRShader->SetInt("u_Steps", s_Data.SSRData.Steps); + s_Data.SSRData.SSRShader->SetFloat("u_MaxDistance", s_Data.SSRData.MaxDistance); + s_Data.SSRData.SSRShader->SetFloat("u_Thickness", s_Data.SSRData.Thickness); + s_Data.SSRData.SSRShader->SetFloat("u_Intensity", s_Data.SSRData.Intensity); + + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } + + void Renderer3D::SSRBlurPass() + { + Renderer::BeginRenderPass(s_Data.SSRData.SSRBlurRenderPass); + + s_Data.SSRData.SSRBlurShader->Bind(); + s_Data.SSRData.SSRBlurShader->SetInt("u_SSRTexture", 0); + s_Data.SSRData.SSRBlurShader->SetInt("u_DepthTexture", 1); + s_Data.SSRData.SSRBlurShader->SetInt("u_MaterialInfoTexture", 2); + + s_Data.SSRData.SSRRenderPass->GetSpecification().TargetFramebuffer->BindTexture(); + s_Data.SSRData.SSRDepthTexture->Bind(1); + s_Data.SSRData.SSRMaterialInfoTexture->Bind(2); + + auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; + s_Data.SSRData.SSRBlurShader->SetFloat2("u_ScreenSize", glm::vec2((float)srcFB->GetWidth(), (float)srcFB->GetHeight())); + s_Data.SSRData.SSRBlurShader->SetFloat("u_BlurRadius", s_Data.SSRData.BlurRadius); + + Renderer::SubmitFullscreenQuad(nullptr); + Renderer::EndRenderPass(); + } + void Renderer3D::ResolveMSAA() { auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer; @@ -1190,23 +1437,75 @@ namespace Prism if (UI::BeginTreeNode("Shadows", false)) { - if (UI::BeginTreeNode("Shadow Map", false)) - { + UI::BeginPropertyGrid(); + UI::Property("Enable", s_Data.ShadowData.ShadowEnabled); + UI::PropertySlider("Bias", s_Data.ShadowData.ShadowBias, 0.0f, 0.1f); + UI::PropertySlider("Softness", s_Data.ShadowData.ShadowSoftness, 0, 3); + UI::PropertySlider("Intensity", s_Data.ShadowData.ShadowIntensity, 0.0f, 2.0f); + UI::EndPropertyGrid(); + if (UI::BeginTreeNode("Cascade Settings", false)) + { UI::BeginPropertyGrid(); - UI::Property("EnableMap", s_Data.ShadowData.ShadowEnabled); - UI::Property("ShadowBias", s_Data.ShadowData.ShadowBias, 0.01f); - UI::PropertySlider("ShadowSoftness", s_Data.ShadowData.ShadowSoftness, 0, 3); - UI::Property("ShadowIntensity", s_Data.ShadowData.ShadowIntensity, 0.01f); - UI::Property("Ortho Size", s_Data.ShadowData.OrthoSize); - // UI::Property("Shadow Distance", s_Data.ShadowD0ata.ShadowDistance); + UI::PropertySlider("Split Lambda", s_Data.ShadowData.CascadeSplitLambda, 0.0f, 1.0f); + UI::PropertySlider("Shadow Far", s_Data.ShadowData.ShadowFarOverride, 1.0f, 1000.0f); + UI::PropertySlider("Depth Extend", s_Data.ShadowData.ShadowDepthExtend, 0.0f, 200.0f); UI::EndPropertyGrid(); - auto fb = s_Data.ShadowData.ShadowMapRenderPass->GetSpecification().TargetFramebuffer; - auto id = fb->GetDepthAttachmentRendererID(); + float cameraNear = s_Data.SceneData.SceneCamera.Camera.GetNear(); + float shadowFar = s_Data.ShadowData.ShadowFar; + float totalRange = shadowFar - cameraNear; - float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f - ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 }); + ImGui::Separator(); + ImGui::Text("Cascade Splits (world distance):"); + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) + { + float splitWorld = cameraNear + s_Data.ShadowData.CascadeSplits[i] * totalRange; + float splitPct = s_Data.ShadowData.CascadeSplits[i] * 100.0f; + ImGui::Text(" Cascade %d: %.2f (%.1f%%)", i, splitWorld, splitPct); + } + + float barWidth = ImGui::GetContentRegionAvail().x; + float barHeight = 20.0f; + ImDrawList* drawList = ImGui::GetWindowDrawList(); + ImVec2 cursor = ImGui::GetCursorScreenPos(); + + ImU32 colors[4] = { + IM_COL32(70, 130, 180, 255), + IM_COL32(60, 179, 113, 255), + IM_COL32(218, 165, 32, 255), + IM_COL32(205, 92, 92, 255) + }; + + float prevSplit = 0.0f; + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) + { + float splitPct = s_Data.ShadowData.CascadeSplits[i]; + float x0 = cursor.x + prevSplit * barWidth; + float x1 = cursor.x + splitPct * barWidth; + drawList->AddRectFilled(ImVec2(x0, cursor.y), ImVec2(x1, cursor.y + barHeight), colors[i]); + char label[16]; + snprintf(label, sizeof(label), "C%d", i); + drawList->AddText(ImVec2((x0 + x1) * 0.5f - 8.0f, cursor.y + 2.0f), IM_COL32(255, 255, 255, 255), label); + prevSplit = splitPct; + } + drawList->AddRect(ImVec2(cursor.x, cursor.y), ImVec2(cursor.x + barWidth, cursor.y + barHeight), IM_COL32(200, 200, 200, 255)); + ImGui::Dummy(ImVec2(barWidth, barHeight)); + + UI::EndTreeNode(); + } + + if (UI::BeginTreeNode("Shadow Map", false)) + { + float size = ImGui::GetContentRegionAvail().x; + for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++) + { + auto fb = s_Data.ShadowData.ShadowMapRenderPass[i]->GetSpecification().TargetFramebuffer; + auto id = fb->GetDepthAttachmentRendererID(); + + ImGui::Text("Cascade %d", i); + ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 }); + } UI::EndTreeNode(); } @@ -1265,8 +1564,35 @@ namespace Prism UI::EndTreeNode(); } + if (UI::BeginTreeNode("SSR", false)) + { + UI::BeginPropertyGrid(); + UI::Property("Enable SSR", s_Data.SSRData.EnableSSR); + if (s_Data.SSRData.EnableSSR) + { + UI::Property("Intensity", s_Data.SSRData.Intensity, 0.01f, 0.0f, 5.0f); + UI::Property("Max Distance", s_Data.SSRData.MaxDistance, 0.1f, 1.0f, 200.0f); + UI::Property("Thickness", s_Data.SSRData.Thickness, 0.01f, 0.01f, 5.0f); + UI::PropertySlider("Steps", s_Data.SSRData.Steps, 1, 128); + UI::Property("Blur Radius", s_Data.SSRData.BlurRadius, 0.1f, 0.0f, 10.0f); + } + UI::EndPropertyGrid(); + + if (s_Data.SSRData.EnableSSR) + { + auto fb = s_Data.SSRData.SSRBlurRenderPass->GetSpecification().TargetFramebuffer; + const auto id = fb->GetColorAttachmentRendererID(); + const float size = ImGui::GetContentRegionAvail().x; + float w = size; + float h = w / ((float)fb->GetWidth() / (float)fb->GetHeight()); + ImGui::Image((ImTextureID)id, { w, h }, { 0, 1 }, { 1, 0 }); + } + + UI::EndTreeNode(); + } + ImGui::End(); } -} \ No newline at end of file +} diff --git a/Prism/src/Prism/Renderer/Renderer3D.h b/Prism/src/Prism/Renderer/Renderer3D.h index 0ae2d19..de7e19b 100644 --- a/Prism/src/Prism/Renderer/Renderer3D.h +++ b/Prism/src/Prism/Renderer/Renderer3D.h @@ -55,6 +55,7 @@ namespace Prism static SceneRendererOptions& GetOptions(); static Ref GetBlackCubeTexture(); static Ref GetBlackTexture(); + static Ref GetBRDFLUT(); private: static void FlushDrawList(Ref& outRenderPass); @@ -62,6 +63,8 @@ namespace Prism static void AutoExposurePass(); static void ShadowMapPass(); static void GeometryPass(); + static void SSRPass(); + static void SSRBlurPass(); static void BloomBlurPass(); static void GridPass(); diff --git a/Prism/src/Prism/Renderer/Texture.cpp b/Prism/src/Prism/Renderer/Texture.cpp index d0c1208..ff118d4 100644 --- a/Prism/src/Prism/Renderer/Texture.cpp +++ b/Prism/src/Prism/Renderer/Texture.cpp @@ -15,6 +15,8 @@ namespace Prism { case TextureFormat::RGB: return 3; case TextureFormat::RGBA: return 4; + case TextureFormat::DEPTH24STENCIL8: return 4; + case TextureFormat::DEPTH32F: return 4; } return 0; } diff --git a/Prism/src/Prism/Renderer/Texture.h b/Prism/src/Prism/Renderer/Texture.h index 27ac159..99fffbf 100644 --- a/Prism/src/Prism/Renderer/Texture.h +++ b/Prism/src/Prism/Renderer/Texture.h @@ -21,6 +21,8 @@ namespace Prism RGBA16F = 3, RG8 = 4, RG16F = 5, + DEPTH24STENCIL8 = 6, + DEPTH32F = 7, }; enum class TextureWrap diff --git a/Prism/src/Prism/Utilities/FileSystem.h b/Prism/src/Prism/Utilities/FileSystem.h index e767e31..f103248 100644 --- a/Prism/src/Prism/Utilities/FileSystem.h +++ b/Prism/src/Prism/Utilities/FileSystem.h @@ -1,11 +1,11 @@ -// -// Created by Atdunbg on 2026/1/21. -// - #ifndef PRISM_FILESYSTEM_H #define PRISM_FILESYSTEM_H #include +#include +#include +#include +#include namespace efsw @@ -50,8 +50,17 @@ namespace Prism static void StartWatching(); static void StopWatching(); + static void ProcessPendingEvents(); + + static void NotifyFileWriting(const std::string& filepath); + private: - static std::atomic s_IgnoreNextChange; + static std::mutex s_EventQueueMutex; + static std::vector s_PendingEvents; + + static std::mutex s_RecentWritesMutex; + static std::unordered_map s_RecentWrites; + static efsw::FileWatcher* s_FileWatcher; static class PrismFileWatcherListener* s_Listener; static FileSystemChangedCallbackFn s_Callback; @@ -62,4 +71,4 @@ namespace Prism } -#endif //PRISM_FILESYSTEM_H \ No newline at end of file +#endif //PRISM_FILESYSTEM_H