add material sphere preview, add ssr, improve shadow

This commit is contained in:
2026-05-14 21:37:02 +08:00
parent d11f8bb0c9
commit f50824bdb1
30 changed files with 1277 additions and 356 deletions

View File

@ -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<ConsolePanel>();
@ -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<PBRMaterialAsset>([&](Ref<PBRMaterialAsset> 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<SkyLightComponent>();
slc.DynamicSky = true;
Ref<TextureCube> preethamEnv = Renderer3D::CreatePreethamSky(slc.TurbidityAzimuthInclination);
slc.SceneEnvironment = Ref<Environment>::Create(preethamEnv, preethamEnv);
}
void EditorLayer::OpenScene()

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

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,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);
}

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,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;
@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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)

View File

@ -91,6 +91,7 @@ namespace Prism
Ref<Texture2D> RoughnessTexture;
bool IsDirty = true;
bool PreviewIsDirty = true;
Ref<Texture2D> PreviewTexture;
};

View File

@ -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();
}
}

View File

@ -13,8 +13,6 @@
#include <yaml-cpp/yaml.h>
#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<std::string>()(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<typename T>
void AssetsManager::ForEachAsset(const std::function<void(Ref<T>)>& callback)
{
for (auto& [handle, asset] : s_LoadedAssets)
{
if (asset->Type == AssetType::Material && asset->IsDataLoaded)
{
Ref<T> typed = asset.As<T>();
if (typed)
callback(typed);
}
}
}
template PRISM_API void AssetsManager::ForEachAsset<PBRMaterialAsset>(const std::function<void(Ref<PBRMaterialAsset>)>&);
std::string AssetsManager::StripExtras(const std::string& filename)
{
@ -383,11 +398,19 @@ namespace Prism
case FileSystemAction::Modified:
{
if (!e.IsDirectory)
{
AssetHandle existingHandle = GetAssetHandleFromFilePath(e.FilePath);
if (existingHandle && s_LoadedAssets[existingHandle]->IsDataLoaded)
{
if (s_AssetsChangeCallback)
s_AssetsChangeCallback();
}
else
{
Ref<Asset> 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<Directory>& dir, const std::string& dirName)
{
if (dir->FileName == dirName)
return dir->Handle;
for (const AssetHandle& childHandle : dir->ChildDirectories)
{
Ref<Directory> child = GetAsset<Directory>(childHandle);
AssetHandle dirHandle = FindParentHandleInChildren(child, dirName);
if (IsAssetHandleValid(dirHandle))
return dirHandle;
}
return 0;
}
AssetHandle AssetsManager::FindParentHandle(const std::string& filepath)
{
const std::vector<std::string> parts = Utils::SplitString(filepath, "/\\");
const std::string& parentFolder = parts[parts.size() - 2];
Ref<Directory> assetsDirectory = GetAsset<Directory>(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);
}
}

View File

@ -63,6 +63,9 @@ namespace Prism
static bool IsAssetType(AssetHandle assetHandle, AssetType type);
template<typename T>
static void ForEachAsset(const std::function<void(Ref<T>)>& 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<Directory>& dir, const std::string& dirName);
static AssetHandle FindParentHandle(const std::string& filepath);
private:

View File

@ -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;
}

View File

@ -29,13 +29,19 @@ namespace Prism
m_AssetIconMap["hdr"] = AssetsManager::GetAsset<Texture2D>("assets/editor/file.png");
m_AssetIconMap["fbx"] = AssetsManager::GetAsset<Texture2D>("assets/editor/fbx.png");
m_AssetIconMap["obj"] = AssetsManager::GetAsset<Texture2D>("assets/editor/obj.png");
m_AssetIconMap["fbx"] = AssetsManager::GetAsset<Texture2D>("assets/editor/fbx.png");
m_AssetIconMap["dae"] = AssetsManager::GetAsset<Texture2D>("assets/editor/model.png");
m_AssetIconMap["wav"] = AssetsManager::GetAsset<Texture2D>("assets/editor/wav.png");
m_AssetIconMap["cs"] = AssetsManager::GetAsset<Texture2D>("assets/editor/csc.png");
m_AssetIconMap["png"] = AssetsManager::GetAsset<Texture2D>("assets/editor/png.png");
m_AssetIconMap["jpg"] = AssetsManager::GetAsset<Texture2D>("assets/editor/texture.png");
m_AssetIconMap["jepg"] = AssetsManager::GetAsset<Texture2D>("assets/editor/texture.png");
m_AssetIconMap["bmp"] = AssetsManager::GetAsset<Texture2D>("assets/editor/texture.png");
m_AssetIconMap["tga"] = AssetsManager::GetAsset<Texture2D>("assets/editor/texture.png");
m_AssetIconMap["scene"] = AssetsManager::GetAsset<Texture2D>("assets/editor/scene.png");
m_AssetIconMap["blend"] = AssetsManager::GetAsset<Texture2D>("assets/editor/blend.png");
// TODO: get a logo for this project
m_AssetIconMap["scene"] = AssetsManager::GetAsset<Texture2D>("assets/editor/asset.png");
m_AssetIconMap["wav"] = AssetsManager::GetAsset<Texture2D>("assets/editor/wav.png");
m_AssetIconMap["pmat"] = AssetsManager::GetAsset<Texture2D>("assets/editor/asset.png");

View File

@ -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<Ref<PBRMaterialAsset>>(asset);
LoadAssetToGraph(); // 加载资产数据到节点图
s_EditingAsset = m_Asset;
LoadAssetToGraph();
}
void PBRMaterialEditor::InitPreviewResources()
static Ref<RenderPass> s_PreviewRenderPass;
static Ref<Mesh> s_PreviewSphere;
static Ref<Material> s_PreviewBaseMaterial;
static Ref<MaterialInstance> s_PreviewMaterialInstance;
static Ref<TextureCube> s_PreviewEnvRadiance;
static Ref<TextureCube> 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<FrameBuffer> 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<PBRMaterialAsset> PBRMaterialEditor::s_EditingAsset;
void PBRMaterialEditor::RenderMaterialPreview(Ref<PBRMaterialAsset> 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));
});
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]()
{
auto fb = s_PreviewRenderPass->GetSpecification().TargetFramebuffer;
auto tex = materialAsset->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<Texture2D>& 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<Texture2D>& 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<Texture2D>& 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<Texture2D>& 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
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()

View File

@ -25,29 +25,18 @@ namespace Prism
Ref<Asset> GetAsset() override { return m_Asset; }
void SetAsset(const Ref<Asset>& asset) override;
static void InitPreviewRenderer();
static void RenderMaterialPreview(Ref<PBRMaterialAsset> materialAsset);
static Ref<PBRMaterialAsset> GetEditingAsset() { return s_EditingAsset; }
private:
Ref<PBRMaterialAsset> m_Asset;
ed::EditorContext* m_EditorContext = nullptr;
static Ref<PBRMaterialAsset> s_EditingAsset;
// 预览贴图
Ref<RenderPass> m_PreviewRenderPass;
Ref<Mesh> m_PreviewSphere;
Ref<MaterialInstance> 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(); // 新的节点编辑器面板

View File

@ -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<StorageBuffer>& 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<FrameBuffer>& src, uint32_t attachmentIndex, const Ref<Texture>& 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<FrameBuffer>& src, const Ref<Texture>& 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<FrameBuffer>& src, const Ref<Texture>& dst) const
{
uint32_t tempFBO;

View File

@ -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<StorageBuffer>& ref) const override;
void ResolveMultisampleTexture(const Ref<FrameBuffer>& src, const Ref<Texture>& dst) const override;
void ResolveMultisampleTexture(const Ref<FrameBuffer>& src, uint32_t attachmentIndex, const Ref<Texture>& dst) const override;
void ResolveMultisampleDepth(const Ref<FrameBuffer>& src, const Ref<Texture>& dst) const override;
void CopyFrameToTexture(const Ref<FrameBuffer>& src, const Ref<Texture>& dst) const override;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -1,7 +1,3 @@
//
// Created by Atdunbg on 2026/1/21.
//
#include <codecvt>
#include "Prism/Utilities/FileSystem.h"
@ -22,7 +18,12 @@ namespace Prism
{
FileSystem::FileSystemChangedCallbackFn FileSystem::s_Callback;
std::atomic<bool> FileSystem::s_IgnoreNextChange = false;
std::mutex FileSystem::s_EventQueueMutex;
std::vector<FileSystemChangedEvent> FileSystem::s_PendingEvents;
std::mutex FileSystem::s_RecentWritesMutex;
std::unordered_map<std::string, std::chrono::steady_clock::time_point> 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<std::mutex> 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<int>(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<int>(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<FileSystemChangedEvent> events;
{
std::lock_guard<std::mutex> lock(s_EventQueueMutex);
events.swap(s_PendingEvents);
}
auto now = std::chrono::steady_clock::now();
{
std::lock_guard<std::mutex> lock(s_RecentWritesMutex);
for (auto it = s_RecentWrites.begin(); it != s_RecentWrites.end(); )
{
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(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<std::mutex> 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<std::mutex> lock(s_RecentWritesMutex);
s_RecentWrites[normalized] = std::chrono::steady_clock::now();
}
void FileSystem::StartWatching()
{

View File

@ -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);

View File

@ -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<uint32_t>(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);
}
}

View File

@ -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<Pipeline> Create(const PipelineSpecification& spec);
};

View File

@ -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<StorageBuffer>& ref) const = 0;
virtual void ResolveMultisampleTexture(const Ref<FrameBuffer>& src, const Ref<Texture>& dst) const = 0;
virtual void ResolveMultisampleTexture(const Ref<FrameBuffer>& src, uint32_t attachmentIndex, const Ref<Texture>& dst) const = 0;
virtual void ResolveMultisampleDepth(const Ref<FrameBuffer>& src, const Ref<Texture>& dst) const = 0;
virtual void CopyFrameToTexture(const Ref<FrameBuffer>& src, const Ref<Texture>& dst) const = 0;

View File

@ -7,6 +7,9 @@
#include "Prism/Core/Timer.h"
#include "Prism/Scene/Scene.h"
#include <limits>
#include <cmath>
#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<Shader> ShadowMapShader, ShadowMapAnimShader;
Ref<RenderPass> ShadowMapRenderPass;
Ref<RenderPass> 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<Shader> SSRShader;
Ref<Shader> SSRBlurShader;
Ref<RenderPass> SSRRenderPass;
Ref<RenderPass> SSRBlurRenderPass;
Ref<Texture2D> SSRDepthTexture;
Ref<Texture2D> SSRMaterialInfoTexture;
} SSRData;
SceneRendererOptions Options;
Ref<TextureCube> 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;
for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++)
{
RenderPassSpecification shadowMapRenderPassSpec;
shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec);
s_Data.ShadowData.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec);
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<Texture2D> Renderer3D::GetBRDFLUT()
{
return s_Data.BRDFLUT;
}
void Renderer3D::FlushDrawList(Ref<RenderPass>& outRenderPass)
{
@ -493,6 +555,12 @@ namespace Prism
ResolveMSAA();
if (s_Data.SSRData.EnableSSR)
{
SSRPass();
SSRBlurPass();
}
BloomBlurPass();
GridPass();
@ -589,43 +657,106 @@ namespace Prism
if (!s_Data.ShadowData.ShadowEnabled || directionalLights.Intensity == 0.0f || !directionalLights.CastShadows)
{
Renderer::BeginRenderPass(s_Data.ShadowData.ShadowMapRenderPass);
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
);
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++)
{
fullFrustumCorners[i] = cameraInvViewProj * ndcCorners[i];
fullFrustumCorners[i] /= fullFrustumCorners[i].w;
}
const glm::mat4 lightSpaceMatrix = lightProj * lightView;
s_Data.LightMatrices = lightSpaceMatrix;
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<float>::max();
float maxX = std::numeric_limits<float>::lowest();
float minY = std::numeric_limits<float>::max();
float maxY = std::numeric_limits<float>::lowest();
float minZ = std::numeric_limits<float>::max();
float maxZ = std::numeric_limits<float>::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);
Renderer::BeginRenderPass(s_Data.ShadowData.ShadowMapRenderPass[cascade]);
for (auto& dc : s_Data.ShadowPassDrawList)
{
@ -641,6 +772,7 @@ namespace Prism
Renderer::EndRenderPass();
}
}
void Renderer3D::GeometryPass()
@ -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,9 +898,11 @@ namespace Prism
auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap");
if (rd)
{
auto reg = rd->GetRegister();
auto tex = s_Data.ShadowData.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
uint32_t baseReg = rd->GetRegister();
for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++)
{
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);
@ -764,6 +910,7 @@ namespace Prism
});
}
}
}
Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.MaterialInstances);
}
@ -832,18 +979,31 @@ 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();
uint32_t baseReg = rd->GetRegister();
for (int i = 0; i < Renderer3DData::CSM_CASCADE_COUNT; i++)
{
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);
@ -851,6 +1011,7 @@ namespace Prism
});
}
}
}
Renderer::SubmitMesh(dc.mesh, dc.Transform, dc.MaterialInstances);
}
@ -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("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::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();
auto fb = s_Data.ShadowData.ShadowMapRenderPass->GetSpecification().TargetFramebuffer;
if (UI::BeginTreeNode("Cascade Settings", false))
{
UI::BeginPropertyGrid();
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();
float cameraNear = s_Data.SceneData.SceneCamera.Camera.GetNear();
float shadowFar = s_Data.ShadowData.ShadowFar;
float totalRange = shadowFar - cameraNear;
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();
float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f
ImGui::Text("Cascade %d", i);
ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 });
}
UI::EndTreeNode();
}
@ -1265,6 +1564,33 @@ 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();
}

View File

@ -55,6 +55,7 @@ namespace Prism
static SceneRendererOptions& GetOptions();
static Ref<TextureCube> GetBlackCubeTexture();
static Ref<Texture2D> GetBlackTexture();
static Ref<Texture2D> GetBRDFLUT();
private:
static void FlushDrawList(Ref<RenderPass>& 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();

View File

@ -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;
}

View File

@ -21,6 +21,8 @@ namespace Prism
RGBA16F = 3,
RG8 = 4,
RG16F = 5,
DEPTH24STENCIL8 = 6,
DEPTH32F = 7,
};
enum class TextureWrap

View File

@ -1,11 +1,11 @@
//
// Created by Atdunbg on 2026/1/21.
//
#ifndef PRISM_FILESYSTEM_H
#define PRISM_FILESYSTEM_H
#include <functional>
#include <mutex>
#include <vector>
#include <chrono>
#include <unordered_map>
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<bool> s_IgnoreNextChange;
static std::mutex s_EventQueueMutex;
static std::vector<FileSystemChangedEvent> s_PendingEvents;
static std::mutex s_RecentWritesMutex;
static std::unordered_map<std::string, std::chrono::steady_clock::time_point> s_RecentWrites;
static efsw::FileWatcher* s_FileWatcher;
static class PrismFileWatcherListener* s_Listener;
static FileSystemChangedCallbackFn s_Callback;