Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions features/Grass Lighting/Shaders/RunGrass.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,6 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
if (lightCount > 0) {
uint lightOffset = lightGrid[clusterIndex].offset;

float2 screenUV = ViewToUV(viewPosition, true, eyeIndex);
float screenNoise = InterleavedGradientNoise(screenUV * perPassLLF[0].BufferDim);

[loop] for (uint i = 0; i < lightCount; i++)
Expand All @@ -540,9 +539,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace

float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection, true, eyeIndex);
if (light.shadowMode == 2)
lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, eyeIndex);
lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, 0.0, eyeIndex);
else if (light.shadowMode == 1)
lightColor *= ContactShadowsLong(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex);
lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex);

float3 lightDiffuseColor = lightColor * saturate(lightAngle.xxx);

Expand Down
58 changes: 20 additions & 38 deletions features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "Common/VR.hlsl"

struct LightGrid
{
uint offset;
Expand Down Expand Up @@ -44,9 +46,10 @@ float GetFarPlane()
return perPassLLF[0].CameraFar;
}

// Get a raw depth from the depth buffer.
float GetDepth(float2 uv)
// Get a raw depth from the depth buffer. [0,1] in uv space
float GetDepth(float2 uv, uint a_eyeIndex = 0)
{
uv = ConvertToStereoUV(uv, a_eyeIndex);
return TexDepthSampler.Load(int3(uv * perPassLLF[0].BufferDim, 0));
}

Expand All @@ -70,53 +73,32 @@ float GetScreenDepth(float depth)
return (perPassLLF[0].CameraData.w / (-depth * perPassLLF[0].CameraData.z + perPassLLF[0].CameraData.x));
}

float GetScreenDepth(float2 uv)
float GetScreenDepth(float2 uv, uint a_eyeIndex = 0)
{
float depth = GetDepth(uv);
float depth = GetDepth(uv, a_eyeIndex);
return GetScreenDepth(depth);
}

float ContactShadows(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS, uint a_eyeIndex = 0)
float ContactShadows(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS, float radius = 0.0, uint a_eyeIndex = 0)
{
lightDirectionVS *= 1.5;

// Offset starting position with interleaved gradient noise
rayPos += lightDirectionVS * offset;

// Accumulate samples
float shadow = 0.0;
[loop] for (uint i = 0; i < 4; i++)
{
// Step the ray
rayPos += lightDirectionVS;
float2 rayUV = ViewToUV(rayPos, true, a_eyeIndex);

// Ensure the UV coordinates are inside the screen
if (!IsSaturated(rayUV))
break;

// Compute the difference between the ray's and the camera's depth
float rayDepth = GetScreenDepth(rayUV);

// Difference between the current ray distance and the marched light
float depthDelta = rayPos.z - rayDepth;
if (rayDepth > 16.5) // First person
shadow += saturate(depthDelta * 0.20) - saturate(depthDelta * 0.1);
float lightDirectionMult = 1.5;
float2 depthDeltaMult = float2(0.20, 0.1);
uint loopMax = 4;

if (radius > 0) { // long
lightDirectionMult = radius / 32;
depthDeltaMult = float2(1.0, 0.05);
loopMax = 32;
}

return 1.0 - saturate(shadow);
}

float ContactShadowsLong(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS, float radius, uint a_eyeIndex = 0)
{
lightDirectionVS *= radius / 32;
lightDirectionVS *= lightDirectionMult;

// Offset starting position with interleaved gradient noise
rayPos += lightDirectionVS * offset;

// Accumulate samples
float shadow = 0.0;
[loop] for (uint i = 0; i < 32; i++)
[loop] for (uint i = 0; i < loopMax; i++)
{
// Step the ray
rayPos += lightDirectionVS;
Expand All @@ -127,12 +109,12 @@ float ContactShadowsLong(float3 rayPos, float2 texcoord, float offset, float3 li
break;

// Compute the difference between the ray's and the camera's depth
float rayDepth = GetScreenDepth(rayUV);
float rayDepth = GetScreenDepth(rayUV, a_eyeIndex);

// Difference between the current ray distance and the marched light
float depthDelta = rayPos.z - rayDepth;
if (rayDepth > 16.5) // First person
shadow += saturate(depthDelta) - saturate(depthDelta * 0.05);
shadow += saturate(depthDelta * depthDeltaMult.x) - saturate(depthDelta * depthDeltaMult.y);
}

return 1.0 - saturate(shadow);
Expand Down
53 changes: 33 additions & 20 deletions package/Shaders/Common/VR.hlsl
Original file line number Diff line number Diff line change
@@ -1,22 +1,35 @@
/*
* Multiply for matrixes that will use an a_eyeIndex.
* This uses a standard mul if in flat
*
* @param a_matrix The matrix to multiple against
* @param a_vector The vector to multiply
* @param a_eyeIndex The a_eyeIndex, normally 0 for flat
* @return The result of a mul that works in VR with eyeindex
/**
Converts to the eye specific uv.
In VR, texture buffers include the left and right eye in the same buffer. Flat only has a single camera for the entire width.
This means the x value [0, .5] represents the left eye, and the y value (.5, 1] are the right eye.
This returns the adjusted value
@param uv - uv coords [0,1] to be encoded for VR
@param a_eyeIndex The eyeIndex; 0 is left, 1 is right
@returns uv with x coords adjusted for the VR texture buffer
*/
float4 NG_mul(float4x4 a_matrix, float4 a_vector, uint a_eyeIndex = 0)
float2 ConvertToStereoUV(float2 uv, uint a_eyeIndex)
{
#if !defined(VR)
float4 result = mul(a_matrix, a_vector);
#else
float4 result;
result.x = dot(a_matrix[a_eyeIndex + 0].xyzw, a_vector.xyzw);
result.y = dot(a_matrix[a_eyeIndex + 1].xyzw, a_vector.xyzw);
result.z = dot(a_matrix[a_eyeIndex + 2].xyzw, a_vector.xyzw);
result.w = dot(a_matrix[a_eyeIndex + 3].xyzw, a_vector.xyzw);
#endif // VR
return result;
}
#ifdef VR
// convert [0,1] to eye specific [0,.5] and [.5, 1] dependent on a_eyeIndex
uv.x = (uv.x + (float)a_eyeIndex) / 2;
#endif
return uv;
}

/**
Converts from eye specific uv to general uv.
In VR, texture buffers include the left and right eye in the same buffer.
This means the x value [0, .5] represents the left eye, and the y value (.5, 1] are the right eye.
This returns the adjusted value
@param uv - eye specific uv coords [0,1]; if uv.x < 0.5, it's a left eye; otherwise right
@param a_eyeIndex The eyeIndex; 0 is left, 1 is right
@returns uv with x coords adjusted to full range for either left or right eye
*/
float2 ConvertFromStereoUV(float2 uv, uint a_eyeIndex)
{
#ifdef VR
// convert [0,.5] to [0, 1] and [.5, 1] to [0,1]
uv.x = 2 * uv.x - (float)a_eyeIndex;
#endif
return uv;
}
6 changes: 3 additions & 3 deletions package/Shaders/Lighting.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -1127,7 +1127,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
// In VR, there is no worldPosition or PreviousWorldPosition as an input. This code is used to determine position
// float4 worldPositionVR;
// worldPositionVR.x =
// (uint)cb13[0].y ? 2 * (-eyeIndex * 0.5 + stereoUV.x) : stereoUV.x;
// (uint)cb13 ? 2 * (-eyeIndex * 0.5 + stereoUV.x) : stereoUV.x;
// worldPositionVR.y = -stereoUV.y * DynamicResolutionParams2.y + 1;
// worldPositionVR.xy = worldPositionVR.xy * float2(2, 2) + float2(-1, -1);
// worldPositionVR.z = input.Position.z;
Expand Down Expand Up @@ -1825,9 +1825,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace
if (!FrameParams.z && FrameParams.y && light.shadowMode) {
float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection, true, eyeIndex);
if (light.shadowMode == 2)
lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, eyeIndex);
lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, 0.0, eyeIndex);
else
lightColor *= ContactShadowsLong(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex);
lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex);
}

# if defined(CPM_AVAILABLE)
Expand Down
21 changes: 15 additions & 6 deletions src/Features/LightLimitFix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,16 @@ void LightLimitFix::Bind()
perPassData.CameraData.w = accumulator->kCamera->GetRuntimeData2().viewFrustum.fFar * accumulator->kCamera->GetRuntimeData2().viewFrustum.fNear;

auto viewport = RE::BSGraphics::State::GetSingleton();
float resolutionX = viewport->screenWidth * viewport->GetRuntimeData().dynamicResolutionCurrentWidthScale;
float resolutionY = viewport->screenHeight * viewport->GetRuntimeData().dynamicResolutionCurrentHeightScale;
if (!screenSpaceShadowsTexture) {
auto shadowMask = RE::BSGraphics::Renderer::GetSingleton()->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kSHADOW_MASK];
D3D11_TEXTURE2D_DESC texDesc{};
shadowMask.texture->GetDesc(&texDesc);
texDesc.Format = DXGI_FORMAT_R16_FLOAT;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_RENDER_TARGET;
screenSpaceShadowsTexture = new Texture2D(texDesc);
}
float resolutionX = screenSpaceShadowsTexture->desc.Width * viewport->GetRuntimeData().dynamicResolutionCurrentWidthScale;
float resolutionY = screenSpaceShadowsTexture->desc.Height * viewport->GetRuntimeData().dynamicResolutionCurrentHeightScale;

perPassData.LightsNear = lightsNear;
perPassData.LightsFar = lightsFar;
Expand Down Expand Up @@ -544,10 +552,11 @@ bool LightLimitFix::AddCachedParticleLights(eastl::vector<LightData>& lightsData

auto scaledTimer = timer * config.flickerSpeed;

light.positionWS[0].x += (float)perlin1.noise1D(scaledTimer) * config.flickerMovement;
light.positionWS[0].y += (float)perlin2.noise1D(scaledTimer) * config.flickerMovement;
light.positionWS[0].z += (float)perlin3.noise1D(scaledTimer) * config.flickerMovement;
light.positionWS[1] = light.positionWS[0];
for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) {
light.positionWS[eyeIndex].x += (float)perlin1.noise1D(scaledTimer) * config.flickerMovement;
light.positionWS[eyeIndex].y += (float)perlin2.noise1D(scaledTimer) * config.flickerMovement;
light.positionWS[eyeIndex].z += (float)perlin3.noise1D(scaledTimer) * config.flickerMovement;
}
dimmer = std::max(0.0f, dimmer - ((float)perlin4.noise1D_01(scaledTimer) * config.flickerIntensity));
}

Expand Down
37 changes: 37 additions & 0 deletions src/Features/LightLimitFix.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct LightLimitFix : Feature
std::uint32_t lightCount = 0;

RE::BSRenderPass* currentPass = nullptr;
Texture2D* screenSpaceShadowsTexture = nullptr;

eastl::hash_map<RE::BSGeometry*, std::pair<RE::NiColorA, ParticleLights::Config&>> queuedParticleLights;
eastl::hash_map<RE::BSGeometry*, std::pair<RE::NiColorA, ParticleLights::Config&>> particleLights;
Expand Down Expand Up @@ -283,3 +284,39 @@ struct LightLimitFix : Feature
}
};
};

template <>
struct fmt::formatter<LightLimitFix::LightData>
{
// Presentation format: 'f' - fixed.
char presentation = 'f';

// Parses format specifications of the form ['f'].
constexpr auto parse(format_parse_context& ctx) -> format_parse_context::iterator
{
auto it = ctx.begin(), end = ctx.end();
if (it != end && (*it == 'f'))
presentation = *it++;

// Check if reached the end of the range:
if (it != end && *it != '}')
throw_format_error("invalid format");

// Return an iterator past the end of the parsed range:
return it;
}

// Formats the point p using the parsed format specification (presentation)
// stored in this formatter.
auto format(const LightLimitFix::LightData& l, format_context& ctx) const -> format_context::iterator
{
// ctx.out() is an output iterator to write to.
return fmt::format_to(ctx.out(), "{{address {:x} color {} radius {} posWS {} {} posVS {} {} shadow {}}}",
reinterpret_cast<uintptr_t>(&l),
(Vector3)l.color,
l.radius,
(Vector3)l.positionWS[0], (Vector3)l.positionWS[1],
(Vector3)l.positionVS[0], (Vector3)l.positionVS[1],
l.shadowMode);
}
};