mirror of
https://github.com/hedge-dev/UnleashedRecomp.git
synced 2025-10-30 07:11:05 +00:00
Add enhanced motion blur.
This commit is contained in:
parent
087b1eb220
commit
facc910200
8 changed files with 190 additions and 63 deletions
|
|
@ -292,6 +292,7 @@ endfunction()
|
|||
|
||||
compile_vertex_shader(copy_vs)
|
||||
compile_pixel_shader(csd_filter_ps)
|
||||
compile_pixel_shader(enhanced_motion_blur_ps)
|
||||
compile_pixel_shader(gaussian_blur_3x3)
|
||||
compile_pixel_shader(gaussian_blur_5x5)
|
||||
compile_pixel_shader(gaussian_blur_7x7)
|
||||
|
|
|
|||
75
UnleashedRecomp/gpu/shader/enhanced_motion_blur_ps.hlsl
Normal file
75
UnleashedRecomp/gpu/shader/enhanced_motion_blur_ps.hlsl
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h"
|
||||
|
||||
#ifdef __spirv__
|
||||
|
||||
#define g_BlurRate vk::RawBufferLoad<float4>(g_PushConstants.PixelShaderConstants + 2400, 0x10)
|
||||
#define g_ViewportSize vk::RawBufferLoad<float4>(g_PushConstants.PixelShaderConstants + 384, 0x10)
|
||||
|
||||
#define sampColor_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 0)
|
||||
#define sampColor_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 192)
|
||||
|
||||
#define sampVelocityMap_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 4)
|
||||
#define sampVelocityMap_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 196)
|
||||
|
||||
#define sampZBuffer_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 8)
|
||||
#define sampZBuffer_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 200)
|
||||
|
||||
#else
|
||||
|
||||
cbuffer PixelShaderConstants : register(b1, space4)
|
||||
{
|
||||
float4 g_BlurRate : packoffset(c150);
|
||||
float4 g_ViewportSize : packoffset(c24);
|
||||
};
|
||||
|
||||
cbuffer SharedConstants : register(b2, space4)
|
||||
{
|
||||
uint sampColor_Texture2DDescriptorIndex : packoffset(c0.x);
|
||||
uint sampColor_SamplerDescriptorIndex : packoffset(c12.x);
|
||||
|
||||
uint sampVelocityMap_Texture2DDescriptorIndex : packoffset(c0.y);
|
||||
uint sampVelocityMap_SamplerDescriptorIndex : packoffset(c12.y);
|
||||
|
||||
uint sampZBuffer_Texture2DDescriptorIndex : packoffset(c0.z);
|
||||
uint sampZBuffer_SamplerDescriptorIndex : packoffset(c12.z);
|
||||
|
||||
DEFINE_SHARED_CONSTANTS();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
float4 main(in float4 position : SV_Position, in float2 texCoord : TEXCOORD0) : SV_Target
|
||||
{
|
||||
Texture2D<float4> sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex];
|
||||
Texture2D<float4> sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex];
|
||||
Texture2D<float4> sampZBuffer = g_Texture2DDescriptorHeap[sampZBuffer_Texture2DDescriptorIndex];
|
||||
|
||||
SamplerState sampColor_s = g_SamplerDescriptorHeap[sampColor_SamplerDescriptorIndex];
|
||||
SamplerState sampVelocityMap_s = g_SamplerDescriptorHeap[sampVelocityMap_SamplerDescriptorIndex];
|
||||
SamplerState sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex];
|
||||
|
||||
float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord, 0).x;
|
||||
float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord, 0);
|
||||
float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0;
|
||||
|
||||
int sampleCount = min(64, round(length(velocity * g_ViewportSize.xy)));
|
||||
float2 sampleOffset = velocity / (float) sampleCount;
|
||||
|
||||
float3 color = sampColor.SampleLevel(sampColor_s, texCoord, 0).rgb;
|
||||
int count = 1;
|
||||
|
||||
for (int i = 1; i <= sampleCount; i++)
|
||||
{
|
||||
float2 sampleCoord = texCoord + sampleOffset * i;
|
||||
float3 sampleColor = sampColor.SampleLevel(sampColor_s, sampleCoord, 0).rgb;
|
||||
float sampleDepth = sampZBuffer.SampleLevel(sampZBuffer_s, sampleCoord, 0).x;
|
||||
|
||||
if (sampleDepth - depth < 0.01)
|
||||
{
|
||||
color += sampleColor;
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return float4(color / count, g_BlurRate.x * saturate(dot(abs(velocity), g_ViewportSize.xy) / 8.0) * saturate(count - 1));
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ float4 main(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0)
|
|||
float2 offset = lerp(offsets[int(scaled)], offsets[min(int(scaled) + 1, 2)], frac(scaled));
|
||||
float offsetScale = 1.0 / 0.75;
|
||||
float weight = ComputeWeight(lerp(-offsetScale, offsetScale, step));
|
||||
color += texture.Sample(samplerState, iTexCoord0.xy + offset) * weight;
|
||||
color += texture.SampleLevel(samplerState, iTexCoord0.xy + offset, 0) * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@
|
|||
#include "shader/copy_vs.hlsl.spirv.h"
|
||||
#include "shader/csd_filter_ps.hlsl.dxil.h"
|
||||
#include "shader/csd_filter_ps.hlsl.spirv.h"
|
||||
#include "shader/enhanced_motion_blur_ps.hlsl.dxil.h"
|
||||
#include "shader/enhanced_motion_blur_ps.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_3x3.hlsl.dxil.h"
|
||||
#include "shader/gaussian_blur_3x3.hlsl.spirv.h"
|
||||
#include "shader/gaussian_blur_5x5.hlsl.dxil.h"
|
||||
|
|
@ -1059,6 +1061,8 @@ static std::unique_ptr<GuestShader> g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT];
|
|||
static std::unique_ptr<GuestShader> g_csdFilterShader;
|
||||
static GuestShader* g_csdShader;
|
||||
|
||||
static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
|
||||
|
||||
#define CREATE_SHADER(NAME) \
|
||||
g_device->createShader( \
|
||||
g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \
|
||||
|
|
@ -1461,6 +1465,9 @@ void Video::CreateHostDevice()
|
|||
g_csdFilterShader = std::make_unique<GuestShader>(ResourceType::PixelShader);
|
||||
g_csdFilterShader->shader = CREATE_SHADER(csd_filter_ps);
|
||||
|
||||
g_enhancedMotionBlurShader = std::make_unique<GuestShader>(ResourceType::PixelShader);
|
||||
g_enhancedMotionBlurShader->shader = CREATE_SHADER(enhanced_motion_blur_ps);
|
||||
|
||||
CreateImGuiBackend();
|
||||
|
||||
auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps);
|
||||
|
|
@ -3902,48 +3909,54 @@ static void ProcSetPixelShader(const RenderCommand& cmd)
|
|||
{
|
||||
GuestShader* shader = cmd.setPixelShader.shader;
|
||||
if (shader != nullptr &&
|
||||
shader->shaderCacheEntry != nullptr &&
|
||||
shader->shaderCacheEntry->hash == 0x4294510C775F4EE8)
|
||||
shader->shaderCacheEntry != nullptr)
|
||||
{
|
||||
size_t shaderIndex = GAUSSIAN_BLUR_3X3;
|
||||
|
||||
switch (Config::DepthOfFieldQuality)
|
||||
if (shader->shaderCacheEntry->hash == 0x4294510C775F4EE8)
|
||||
{
|
||||
case EDepthOfFieldQuality::Low:
|
||||
shaderIndex = GAUSSIAN_BLUR_3X3;
|
||||
break;
|
||||
size_t shaderIndex = GAUSSIAN_BLUR_3X3;
|
||||
|
||||
case EDepthOfFieldQuality::Medium:
|
||||
shaderIndex = GAUSSIAN_BLUR_5X5;
|
||||
break;
|
||||
|
||||
case EDepthOfFieldQuality::High:
|
||||
shaderIndex = GAUSSIAN_BLUR_7X7;
|
||||
break;
|
||||
|
||||
case EDepthOfFieldQuality::Ultra:
|
||||
shaderIndex = GAUSSIAN_BLUR_9X9;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
size_t height = round(g_swapChain->getHeight() * Config::ResolutionScale);
|
||||
|
||||
// Use the middle point between resolutions to have the transition less noticable.
|
||||
if (height >= ((2160 + 1440) / 2))
|
||||
shaderIndex = GAUSSIAN_BLUR_9X9;
|
||||
else if (height >= ((1440 + 1080) / 2))
|
||||
shaderIndex = GAUSSIAN_BLUR_7X7;
|
||||
else if (height >= ((1080 + 720) / 2))
|
||||
shaderIndex = GAUSSIAN_BLUR_5X5;
|
||||
else
|
||||
switch (Config::DepthOfFieldQuality)
|
||||
{
|
||||
case EDepthOfFieldQuality::Low:
|
||||
shaderIndex = GAUSSIAN_BLUR_3X3;
|
||||
break;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
case EDepthOfFieldQuality::Medium:
|
||||
shaderIndex = GAUSSIAN_BLUR_5X5;
|
||||
break;
|
||||
|
||||
shader = g_gaussianBlurShaders[shaderIndex].get();
|
||||
case EDepthOfFieldQuality::High:
|
||||
shaderIndex = GAUSSIAN_BLUR_7X7;
|
||||
break;
|
||||
|
||||
case EDepthOfFieldQuality::Ultra:
|
||||
shaderIndex = GAUSSIAN_BLUR_9X9;
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
size_t height = round(g_swapChain->getHeight() * Config::ResolutionScale);
|
||||
|
||||
// Use the middle point between resolutions to have the transition less noticable.
|
||||
if (height >= ((2160 + 1440) / 2))
|
||||
shaderIndex = GAUSSIAN_BLUR_9X9;
|
||||
else if (height >= ((1440 + 1080) / 2))
|
||||
shaderIndex = GAUSSIAN_BLUR_7X7;
|
||||
else if (height >= ((1080 + 720) / 2))
|
||||
shaderIndex = GAUSSIAN_BLUR_5X5;
|
||||
else
|
||||
shaderIndex = GAUSSIAN_BLUR_3X3;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
shader = g_gaussianBlurShaders[shaderIndex].get();
|
||||
}
|
||||
else if (shader->shaderCacheEntry->hash == 0x6B9732B4CD7E7740 && Config::MotionBlur == EMotionBlur::Enhanced)
|
||||
{
|
||||
shader = g_enhancedMotionBlurShader.get();
|
||||
}
|
||||
}
|
||||
|
||||
SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, shader);
|
||||
|
|
@ -5474,6 +5487,12 @@ static void ModelConsumerThread()
|
|||
if (Config::GITextureFiltering == EGITextureFiltering::Bicubic)
|
||||
pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER;
|
||||
|
||||
auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate, const char* name)
|
||||
{
|
||||
SanitizePipelineState(pipelineStateToCreate);
|
||||
EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, emptyHolderPair, name);
|
||||
};
|
||||
|
||||
// Compile both MSAA and non MSAA variants to work with reflection maps. The render formats are an assumption but it should hold true.
|
||||
if (Config::AntiAliasing != EAntiAliasing::None &&
|
||||
pipelineState.renderTargetFormat == RenderFormat::R16G16B16A16_FLOAT &&
|
||||
|
|
@ -5489,34 +5508,40 @@ static void ModelConsumerThread()
|
|||
msaaPipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE;
|
||||
}
|
||||
|
||||
SanitizePipelineState(msaaPipelineState);
|
||||
EnqueueGraphicsPipelineCompilation(msaaPipelineState, emptyHolderPair, "Precompiled Pipeline MSAA");
|
||||
createGraphicsPipeline(msaaPipelineState, "Precompiled Pipeline MSAA");
|
||||
}
|
||||
|
||||
// Compile the custom gaussian blur shaders that we pass to the game.
|
||||
if (pipelineState.pixelShader != nullptr &&
|
||||
pipelineState.pixelShader->shaderCacheEntry != nullptr &&
|
||||
pipelineState.pixelShader->shaderCacheEntry->hash == 0x4294510C775F4EE8)
|
||||
pipelineState.pixelShader->shaderCacheEntry != nullptr)
|
||||
{
|
||||
for (auto& shader : g_gaussianBlurShaders)
|
||||
XXH64_hash_t hash = pipelineState.pixelShader->shaderCacheEntry->hash;
|
||||
|
||||
// Compile the custom gaussian blur shaders that we pass to the game.
|
||||
if (hash == 0x4294510C775F4EE8)
|
||||
{
|
||||
pipelineState.pixelShader = shader.get();
|
||||
SanitizePipelineState(pipelineState);
|
||||
EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Gaussian Blur Pipeline");
|
||||
for (auto& shader : g_gaussianBlurShaders)
|
||||
{
|
||||
auto newPipelineState = pipelineState;
|
||||
newPipelineState.pixelShader = shader.get();
|
||||
createGraphicsPipeline(newPipelineState, "Precompiled Gaussian Blur Pipeline");
|
||||
}
|
||||
}
|
||||
// Compile enhanced motion blur shader.
|
||||
else if (hash == 0x6B9732B4CD7E7740)
|
||||
{
|
||||
auto newPipelineState = pipelineState;
|
||||
newPipelineState.pixelShader = g_enhancedMotionBlurShader.get();
|
||||
createGraphicsPipeline(newPipelineState, "Precompiled Enhanced Motion Blur Pipeline");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SanitizePipelineState(pipelineState);
|
||||
EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline");
|
||||
}
|
||||
|
||||
createGraphicsPipeline(pipelineState, "Precompiled Pipeline");
|
||||
|
||||
// Compile the CSD filter shader that we pass to the game when point filtering is used.
|
||||
if (pipelineState.pixelShader == g_csdShader)
|
||||
{
|
||||
pipelineState.pixelShader = g_csdFilterShader.get();
|
||||
SanitizePipelineState(pipelineState);
|
||||
EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled CSD Filter Pipeline");
|
||||
createGraphicsPipeline(pipelineState, "Precompiled CSD Filter Pipeline");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -319,10 +319,10 @@ CONFIG_DEFINE_ENUM_LOCALE(EDepthOfFieldQuality)
|
|||
{
|
||||
ELanguage::English,
|
||||
{
|
||||
{ EDepthOfFieldQuality::Auto, { "AUTO", "" } },
|
||||
{ EDepthOfFieldQuality::Low, { "LOW", "" } },
|
||||
{ EDepthOfFieldQuality::Medium, { "MEDIUM", "" } },
|
||||
{ EDepthOfFieldQuality::High, { "HIGH", "" } },
|
||||
{ EDepthOfFieldQuality::Auto, { "AUTO", "" } },
|
||||
{ EDepthOfFieldQuality::Low, { "LOW", "" } },
|
||||
{ EDepthOfFieldQuality::Medium, { "MEDIUM", "" } },
|
||||
{ EDepthOfFieldQuality::High, { "HIGH", "" } },
|
||||
{ EDepthOfFieldQuality::Ultra, { "ULTRA", "" } },
|
||||
}
|
||||
}
|
||||
|
|
@ -333,6 +333,18 @@ CONFIG_DEFINE_LOCALE(MotionBlur)
|
|||
{ ELanguage::English, { "Motion Blur", "Use per-object motion blur and radial blur." } }
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_LOCALE(EMotionBlur)
|
||||
{
|
||||
{
|
||||
ELanguage::English,
|
||||
{
|
||||
{ EMotionBlur::Off, { "OFF", "" } },
|
||||
{ EMotionBlur::Original, { "ORIGINAL", "" } },
|
||||
{ EMotionBlur::Enhanced, { "ENHANCED", "" } }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_LOCALE(XboxColourCorrection)
|
||||
{
|
||||
{ ELanguage::English, { "Xbox Color Correction", "Use the warm tint from the Xbox version of the game." } }
|
||||
|
|
|
|||
|
|
@ -26,5 +26,5 @@ void CSDAspectRatioMidAsmHook(PPCRegister& f1, PPCRegister& f2)
|
|||
|
||||
bool MotionBlurMidAsmHook()
|
||||
{
|
||||
return Config::MotionBlur;
|
||||
return Config::MotionBlur != EMotionBlur::Off;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ public:
|
|||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EShadowResolution, ShadowResolution, EShadowResolution::x4096);
|
||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EGITextureFiltering, GITextureFiltering, EGITextureFiltering::Bicubic);
|
||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EDepthOfFieldQuality, DepthOfFieldQuality, EDepthOfFieldQuality::Auto);
|
||||
CONFIG_DEFINE_LOCALISED("Video", bool, MotionBlur, true);
|
||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EMotionBlur, MotionBlur, EMotionBlur::Original);
|
||||
CONFIG_DEFINE_LOCALISED("Video", bool, XboxColourCorrection, false);
|
||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EMovieScaleMode, MovieScaleMode, EMovieScaleMode::Fit);
|
||||
CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIScaleMode, UIScaleMode, EUIScaleMode::Centre);
|
||||
|
|
|
|||
|
|
@ -203,13 +203,27 @@ enum class EDepthOfFieldQuality : uint32_t
|
|||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EDepthOfFieldQuality)
|
||||
{
|
||||
{ "Auto", EDepthOfFieldQuality::Auto },
|
||||
{ "Low", EDepthOfFieldQuality::Low },
|
||||
{ "Medium", EDepthOfFieldQuality::Medium },
|
||||
{ "High", EDepthOfFieldQuality::High },
|
||||
{ "Auto", EDepthOfFieldQuality::Auto },
|
||||
{ "Low", EDepthOfFieldQuality::Low },
|
||||
{ "Medium", EDepthOfFieldQuality::Medium },
|
||||
{ "High", EDepthOfFieldQuality::High },
|
||||
{ "Ultra", EDepthOfFieldQuality::Ultra }
|
||||
};
|
||||
|
||||
enum class EMotionBlur : uint32_t
|
||||
{
|
||||
Off,
|
||||
Original,
|
||||
Enhanced
|
||||
};
|
||||
|
||||
CONFIG_DEFINE_ENUM_TEMPLATE(EMotionBlur)
|
||||
{
|
||||
{ "Off", EMotionBlur::Off },
|
||||
{ "Original", EMotionBlur::Original },
|
||||
{ "Enhanced", EMotionBlur::Enhanced }
|
||||
};
|
||||
|
||||
enum class EMovieScaleMode : uint32_t
|
||||
{
|
||||
Stretch,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue