From 7316a35b0020ef1358022420b37fe93987781cda Mon Sep 17 00:00:00 2001 From: Wls50 Date: Fri, 28 Nov 2025 23:36:26 +0100 Subject: [PATCH] support specular occlusion using bent normals --- betterRenderer/renderer/source/ssao.cpp | 33 +----- .../shaders/manul/gbuffer_lights.hlsl | 106 ------------------ .../shaders/manul/gbuffer_ssao.hlsli | 9 +- betterRenderer/shaders/manul/lighting.hlsli | 13 ++- betterRenderer/shaders/project.manul | 11 +- 5 files changed, 24 insertions(+), 148 deletions(-) delete mode 100644 betterRenderer/shaders/manul/gbuffer_lights.hlsl diff --git a/betterRenderer/renderer/source/ssao.cpp b/betterRenderer/renderer/source/ssao.cpp index 76c697b3..2a1ccd67 100644 --- a/betterRenderer/renderer/source/ssao.cpp +++ b/betterRenderer/renderer/source/ssao.cpp @@ -78,7 +78,7 @@ void NvSsao::Init() { .setWidth(m_width) .setHeight(m_height) .setIsUAV(true) - .setFormat(Format::R8_UINT) + .setFormat(Format::R32_UINT) .setInitialState(ResourceStates::ShaderResource) .setKeepInitialState(true)); @@ -88,7 +88,7 @@ void NvSsao::Init() { .setWidth(m_width) .setHeight(m_height) .setIsUAV(true) - .setFormat(Format::R8_UINT) + .setFormat(Format::R32_UINT) .setInitialState(ResourceStates::ShaderResource) .setKeepInitialState(true)); @@ -98,7 +98,7 @@ void NvSsao::Init() { .setWidth(m_width) .setHeight(m_height) .setIsUAV(true) - .setFormat(Format::R8_UNORM) + .setFormat(Format::RGBA8_UNORM) .setIsTypeless(true) .setInitialState(ResourceStates::ShaderResource) .setKeepInitialState(true)); @@ -129,27 +129,6 @@ void NvSsao::Init() { .addBindingLayout(blPrefilterDepths)); } - { - BindingLayoutHandle blPrefilterDepths; - BindingSetDesc bsDescPrefilter = - BindingSetDesc() - .addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer)) - .addItem(BindingSetItem::Texture_SRV(0, m_gbuffer->m_gbuffer_depth)) - .addItem(BindingSetItem::Sampler(0, sampler_point)); - for (int i = 0; i < XE_GTAO_DEPTH_MIP_LEVELS; ++i) { - bsDescPrefilter.addItem( - BindingSetItem::Texture_UAV(i, m_workingDepths, Format::UNKNOWN, - TextureSubresourceSet(i, 1, 0, 1))); - } - utils::CreateBindingSetAndLayout(m_backend->GetDevice(), - ShaderType::Compute, 0, bsDescPrefilter, - blPrefilterDepths, m_BSPrefilterDepths); - m_PSOPrefilterDepths = m_backend->GetDevice()->createComputePipeline( - ComputePipelineDesc() - .setComputeShader(m_CSPrefilterDepths16x16) - .addBindingLayout(blPrefilterDepths)); - } - { BindingLayoutHandle blGTAO; utils::CreateBindingSetAndLayout( @@ -157,8 +136,6 @@ void NvSsao::Init() { BindingSetDesc() .addItem(BindingSetItem::ConstantBuffer(0, m_constantBuffer)) .addItem(BindingSetItem ::Texture_SRV(0, m_workingDepths)) - .addItem( - BindingSetItem::Texture_SRV(1, m_gbuffer->m_gbuffer_normal)) .addItem(BindingSetItem::Texture_UAV(0, m_workingAOTerm)) .addItem(BindingSetItem::Texture_UAV(1, m_workingEdges)) .addItem(BindingSetItem::Texture_UAV(2, m_debugImage)) @@ -251,10 +228,10 @@ void NvSsao::Render(nvrhi::ICommandList* command_list, nvrhi::BindingSetItem::ConstantBuffer(0, m_constantBuffer)) .addItem(nvrhi::BindingSetItem::Texture_SRV(0, ping)) .addItem(nvrhi::BindingSetItem::Texture_SRV( - 1, m_gbuffer->m_gbuffer_normal)) + 1, m_workingEdges)) .addItem(nvrhi::BindingSetItem::Texture_UAV( 0, last_pass ? m_outputAO.Get() : pong, - nvrhi::Format::R8_UINT)) + nvrhi::Format::R32_UINT)) .addItem(nvrhi::BindingSetItem::Texture_UAV(1, m_workingEdges)) .addItem(nvrhi::BindingSetItem::Texture_UAV(2, m_debugImage)) .addItem(nvrhi::BindingSetItem::Sampler(0, m_SamplerPoint)), diff --git a/betterRenderer/shaders/manul/gbuffer_lights.hlsl b/betterRenderer/shaders/manul/gbuffer_lights.hlsl deleted file mode 100644 index 35e9b727..00000000 --- a/betterRenderer/shaders/manul/gbuffer_lights.hlsl +++ /dev/null @@ -1,106 +0,0 @@ -#include "math.hlsli" - -#include "lighting_functions.hlsli" - -#include "color_transform.hlsli" - -#include "manul/gbuffer_ssao.hlsli" - -struct ViewConstants { - float4x4 m_inverse_model_view; - float4x4 m_inverse_projection; -}; - -struct PushConstantsSpotLight { - float3 m_origin; - float m_radius_sqr; - float3 m_direction; - float m_cos_inner_cone; - float3 m_color; - float m_cos_outer_cone; -}; - -cbuffer g_ViewConst : register(b0) { ViewConstants g_View; } - -#ifdef SPIRV -[[vk::push_constant]] ConstantBuffer g_SpotLight; -#else -cbuffer g_SpotLightConst : register(b1) { PushConstantsSpotLight g_SpotLight; } -#endif - -RWTexture2D g_OutDiffuse : register(u0); - -Texture2D g_GbufferDepth : register(t0); -Texture2D g_GbufferNormal : register(t1); -Texture2D g_GbufferParams : register(t2); -Texture2D g_GbufferColor : register(t3); - -float2 PixelToCS(in float2 pixel, in float2 size) { - return ((pixel + .5) / size - .5) * float2(2., -2.); -} - -float3 ReconstructPos(in float2 cs, in float depth) { - float4 ndc = float4(cs, depth, 1.); - ndc = mul(g_View.m_inverse_projection, ndc); - return ndc.xyz / ndc.w; -} - -[numthreads(8, 8, 1)] -void CS_Clear(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) { - uint2 pixel = PixCoord.xy; - g_OutDiffuse[pixel].rgb = 0.; -} - -[numthreads(8, 8, 1)] -void CS_Spotlight(uint3 PixCoord : SV_DispatchThreadID, uint3 GroupID : SV_GroupID, uint GroupIndex : SV_GroupIndex) { - uint2 gbuffer_dimensions; - uint2 pixel = PixCoord.xy; - - g_OutDiffuse.GetDimensions(gbuffer_dimensions.x, gbuffer_dimensions.y); - float3 position = ReconstructPos(PixelToCS(PixCoord.xy, gbuffer_dimensions), g_GbufferDepth[pixel]); - float3 view = mul((float3x3)g_View.m_inverse_model_view, position); - - float3 L_offset = g_SpotLight.m_origin - view; - float3 L = normalize(L_offset); - if(dot(L_offset, L_offset) > g_SpotLight.m_radius_sqr) return; - - float3 albedo = g_GbufferColor[pixel]; - float4 params = g_GbufferParams[pixel]; - float3 normal = UnpackNormalXYZ(g_GbufferNormal[pixel]); - - SurfaceData surface_data; - surface_data.m_Albedo = albedo; - surface_data.m_Alpha = 1.; - surface_data.m_Metalness = params.r; - surface_data.m_Roughness = params.g; -#ifdef GBUFFER_SSAO_HLSLI - surface_data.m_DiffuseOcclusion = min(params.b, GetSSAO(pixel)); -#else - surface_data.m_DiffuseOcclusion = params.b; -#endif - surface_data.m_Normal = mul((float3x3)g_View.m_inverse_model_view, normal); - surface_data.m_View = -normalize(view); - surface_data.m_Reflect = reflect(-surface_data.m_View, surface_data.m_Normal); - surface_data.m_NdotV = max(dot(surface_data.m_Normal, surface_data.m_View), 0.); - surface_data.m_SpecularF0 = float3(.04, .04, .04) * params.a * 2.; - surface_data.m_SpecularF0 = lerp(surface_data.m_SpecularF0, surface_data.m_Albedo, surface_data.m_Metalness); - surface_data.m_SpecularF = FresnelSchlickRoughness(surface_data.m_NdotV, surface_data.m_SpecularF0, surface_data.m_Roughness); - surface_data.m_HorizonFading = 1.6; - surface_data.m_SpecularOcclusion = ComputeSpecOcclusion(surface_data.m_NdotV, surface_data.m_DiffuseOcclusion, surface_data.m_Roughness); - - float3 lit = float3(0., 0., 0.); - - DirectionalLight directionalLight; - directionalLight.m_LightVector = L; - directionalLight.m_Color = max(REC709_to_XYZ(g_SpotLight.m_color), 0.); - ApplyDirectionalLight(lit, directionalLight, surface_data, 1.); - - - float cone = saturate((dot(L, -g_SpotLight.m_direction) - g_SpotLight.m_cos_inner_cone) / (g_SpotLight.m_cos_outer_cone - g_SpotLight.m_cos_inner_cone)); - float attenuation = 1. / dot(L_offset, L_offset) * (1. - cone); - - g_OutDiffuse[pixel].rgb += lit * attenuation; -} - - - diff --git a/betterRenderer/shaders/manul/gbuffer_ssao.hlsli b/betterRenderer/shaders/manul/gbuffer_ssao.hlsli index ad84a45b..c5049a76 100644 --- a/betterRenderer/shaders/manul/gbuffer_ssao.hlsli +++ b/betterRenderer/shaders/manul/gbuffer_ssao.hlsli @@ -1,6 +1,6 @@ #ifndef GBUFFER_SSAO_HLSLI #define GBUFFER_SSAO_HLSLI -Texture2D g_SSAO : register(t5); +Texture2D g_SSAO : register(t5); float4 R8G8B8A8_UNORM_to_FLOAT4( uint packedInput ) { @@ -19,8 +19,11 @@ void DecodeVisibilityBentNormal( const uint packedValue, out float visibility, o visibility = decoded.w; } -float GetSSAO(in uint2 pixel_position) { - return g_SSAO[pixel_position]; +float4 GetBentNormal(in uint2 pixel_position) { + float4 bent_normal = g_SSAO[pixel_position]; + bent_normal.xyz = 2. * bent_normal.xyz - 1.; + bent_normal.z *= -1.; + return bent_normal; } #endif diff --git a/betterRenderer/shaders/manul/lighting.hlsli b/betterRenderer/shaders/manul/lighting.hlsli index 1e8db7ce..647aeb5a 100644 --- a/betterRenderer/shaders/manul/lighting.hlsli +++ b/betterRenderer/shaders/manul/lighting.hlsli @@ -39,6 +39,12 @@ void ApplyMaterialLighting(out float4 lit, in MaterialData material) { // Camera-centric world position float3 view = mul((float3x3)g_InverseModelView, material.m_Position); + +#ifdef GBUFFER_SSAO_HLSLI + float4 bent_normal = GetBentNormal(pixel_position); + bent_normal.a = min(material.m_MaterialParams.b, bent_normal.a); + bent_normal.xyz = mul((float3x3)g_InverseModelView, bent_normal.xyz); +#endif // Convert material to surface data SurfaceData surface_data; @@ -47,7 +53,7 @@ void ApplyMaterialLighting(out float4 lit, in MaterialData material) surface_data.m_Metalness = material.m_MaterialParams.r; surface_data.m_Roughness = material.m_MaterialParams.g; #ifdef GBUFFER_SSAO_HLSLI - surface_data.m_DiffuseOcclusion = min(material.m_MaterialParams.b, GetSSAO(pixel_position)); + surface_data.m_DiffuseOcclusion = bent_normal.a; #else surface_data.m_DiffuseOcclusion = material.m_MaterialParams.b; #endif @@ -59,7 +65,12 @@ void ApplyMaterialLighting(out float4 lit, in MaterialData material) surface_data.m_SpecularF0 = lerp(surface_data.m_SpecularF0, surface_data.m_Albedo, surface_data.m_Metalness); surface_data.m_SpecularF = FresnelSchlickRoughness(surface_data.m_NdotV, surface_data.m_SpecularF0, surface_data.m_Roughness); surface_data.m_HorizonFading = 1.6; +#ifdef GBUFFER_SSAO_HLSLI + float BNdotR = max(0., dot(surface_data.m_Reflect, bent_normal.xyz)); + surface_data.m_SpecularOcclusion = ComputeSpecOcclusion(BNdotR, surface_data.m_DiffuseOcclusion, surface_data.m_Roughness); +#else surface_data.m_SpecularOcclusion = ComputeSpecOcclusion(surface_data.m_NdotV, surface_data.m_DiffuseOcclusion, surface_data.m_Roughness); +#endif lit.rgb = material.m_MaterialEmission * surface_data.m_Alpha; lit.a = 1. - (saturate((1. - surface_data.m_Alpha) * lerp(1. - dot(surface_data.m_SpecularF, 1./3.), 0., surface_data.m_Metalness))); diff --git a/betterRenderer/shaders/project.manul b/betterRenderer/shaders/project.manul index 2eeae4bd..92cde1ff 100644 --- a/betterRenderer/shaders/project.manul +++ b/betterRenderer/shaders/project.manul @@ -13,6 +13,7 @@ templates: VA_COMPILED_AS_SHADER_CODE: 1 VA_SATURATE: saturate XE_GTAO_GENERATE_NORMALS_INPLACE: 1 + XE_GTAO_COMPUTE_BENT_NORMALS: 1 envmap: source: envmap target: compute @@ -22,9 +23,6 @@ templates: bloom: source: manul/bloom target: compute - gbuffer_lights: - source: manul/gbuffer_lights - target: compute shaders: materials: # Material shaders @@ -327,13 +325,6 @@ shaders: source: manul/line target: pixel entrypoint: pix_main -# Deferred light primitives - gbuffer_light_clear: - use_template: gbuffer_lights - entrypoint: CS_Clear - gbuffer_light_spot: - use_template: gbuffer_lights - entrypoint: CS_Spotlight # Forward+ light culling forwardplus_compute_frustums: source: manul/forward_plus/forward_plus