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

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)