diff --git a/Build Release.bat b/Build Release.bat index c46d364f2..62a75e087 100644 --- a/Build Release.bat +++ b/Build Release.bat @@ -2,7 +2,7 @@ RMDIR dist /S /Q -cmake -S . --preset=FLATRIM --check-stamp-file "build\CMakeFiles\generate.stamp" +cmake -S . --preset=ALL --check-stamp-file "build\CMakeFiles\generate.stamp" if %ERRORLEVEL% NEQ 0 exit 1 cmake --build build --config Release if %ERRORLEVEL% NEQ 0 exit 1 diff --git a/extern/CommonLibSSE-NG b/extern/CommonLibSSE-NG index 887c11060..771a38ae5 160000 --- a/extern/CommonLibSSE-NG +++ b/extern/CommonLibSSE-NG @@ -1 +1 @@ -Subproject commit 887c11060ce7866e4c6b6c7e749a3b8404d42d2e +Subproject commit 771a38ae5510ac9cabd2be1b2d613ad756bd7d94 diff --git a/features/Grass Lighting/Shaders/RunGrass.hlsl b/features/Grass Lighting/Shaders/RunGrass.hlsl index 3b6969aba..c1722f46c 100644 --- a/features/Grass Lighting/Shaders/RunGrass.hlsl +++ b/features/Grass Lighting/Shaders/RunGrass.hlsl @@ -1,6 +1,6 @@ +#include "Common/Color.hlsl" #include "Common/FrameBuffer.hlsl" #include "Common/MotionBlur.hlsl" -#include "Common/Color.hlsl" struct VS_INPUT { @@ -130,7 +130,7 @@ cbuffer cb13 : register(b13) { float4 cb13[3]; } -# endif // VR +# endif // VR # define M_PI 3.1415925 // PI # define M_2PI 6.283185 // PI * 2 @@ -442,9 +442,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace // Swaps direction of the backfaces otherwise they seem to get lit from the wrong direction. if (!frontFace) worldNormal.xyz = -worldNormal.xyz; - + worldNormal.xyz = normalize(lerp(worldNormal.xyz, normalize(input.FlatNormal.xyz), saturate(input.FlatNormal.w))); - + if (complex) { float3 normalColor = float4(TransformNormal(specColor.xyz), 1); // Inverting x as well as y seems to look more correct. @@ -481,7 +481,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 dirDiffuseColor = dirLightColor * saturate(dirLightAngle); lightsDiffuseColor += dirDiffuseColor; - + // Generated texture to simulate light transport. // Numerous attempts were made to use a more interesting algorithm however they were mostly fruitless. float3 subsurfaceColor = normalize(baseColor.xyz); @@ -501,44 +501,43 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 viewPosition = mul(CameraView[eyeIndex], float4(input.WorldPosition.xyz, 1)).xyz; float clampedDepth = clamp(viewPosition.z, GetNearPlane(), GetFarPlane()); - - uint clusterZ = uint(max((log2(clampedDepth) - log2(GetNearPlane())) * 24.0 / log2(GetFarPlane() / GetNearPlane()), 0.0)); + + uint clusterZ = uint(max((log2(clampedDepth) - log2(GetNearPlane())) * 24.0 / log2(GetFarPlane() / GetNearPlane()), 0.0)); uint2 clusterDim = ceil(perPassLLF[0].BufferDim / float2(16, 8)); uint3 cluster = uint3(uint2(input.HPosition.xy / clusterDim), clusterZ); - uint clusterIndex = cluster.x + (16 * cluster.y) + (16 * 8 * cluster.z); - + uint clusterIndex = cluster.x + (16 * cluster.y) + (16 * 8 * cluster.z); + uint lightCount = lightGrid[clusterIndex].lightCount; - if (lightCount > 0){ + if (lightCount > 0) { uint lightOffset = lightGrid[clusterIndex].offset; - float2 screenUV = ViewToUV(viewPosition); + float2 screenUV = ViewToUV(viewPosition, true, eyeIndex); float screenNoise = InterleavedGradientNoise(screenUV * perPassLLF[0].BufferDim); - - [loop] - for (uint i = 0; i < lightCount; i++) - { + + [loop] for (uint i = 0; i < lightCount; i++) + { uint light_index = lightList[lightOffset + i]; StructuredLight light = lights[light_index]; - float3 lightDirection = light.positionWS.xyz - input.WorldPosition.xyz; + float3 lightDirection = light.positionWS[eyeIndex].xyz - input.WorldPosition.xyz; float lightDist = length(lightDirection); float intensityFactor = saturate(lightDist / light.radius); if (intensityFactor == 1) continue; - float intensityMultiplier = 1 - intensityFactor * intensityFactor; + float intensityMultiplier = 1 - intensityFactor * intensityFactor; float3 lightColor = light.color.xyz; float3 nsLightColor = lightColor; float3 normalizedLightDirection = normalize(lightDirection); float lightAngle = dot(worldNormal.xyz, normalizedLightDirection.xyz); - float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection); + float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection, true, eyeIndex); if (light.shadowMode == 2) - lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS); + lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, eyeIndex); else if (light.shadowMode == 1) - lightColor *= ContactShadowsLong(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius); + lightColor *= ContactShadowsLong(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex); float3 lightDiffuseColor = lightColor * saturate(lightAngle.xxx); diff --git a/features/Light Limit Fix/Shaders/LightLimitFix/ClusterCullingCS.hlsl b/features/Light Limit Fix/Shaders/LightLimitFix/ClusterCullingCS.hlsl index d70581929..fc2597e33 100644 --- a/features/Light Limit Fix/Shaders/LightLimitFix/ClusterCullingCS.hlsl +++ b/features/Light Limit Fix/Shaders/LightLimitFix/ClusterCullingCS.hlsl @@ -1,88 +1,85 @@ #include "Common.hlsli" -//references +//references //https://github.com/pezcode/Cluster -StructuredBuffer clusters : register(t0); -StructuredBuffer lights : register(t1); +StructuredBuffer clusters : register(t0); +StructuredBuffer lights : register(t1); -RWStructuredBuffer lightIndexCounter : register(u0); //1 -RWStructuredBuffer lightIndexList : register(u1); //MAX_CLUSTER_LIGHTS * 16^3 -RWStructuredBuffer lightGrid : register(u2); //16^3 +RWStructuredBuffer lightIndexCounter : register(u0); //1 +RWStructuredBuffer lightIndexList : register(u1); //MAX_CLUSTER_LIGHTS * 16^3 +RWStructuredBuffer lightGrid : register(u2); //16^3 groupshared StructuredLight sharedLights[GROUP_SIZE]; bool LightIntersectsCluster(StructuredLight light, ClusterAABB cluster) { - float3 closest = max(cluster.minPoint, min(light.positionVS.xyz, cluster.maxPoint)).xyz; + // For now, only use left eye position + float3 closest = max(cluster.minPoint, min(light.positionVS[0].xyz, cluster.maxPoint)).xyz; - float3 dist = closest - light.positionVS.xyz; - return dot(dist, dist) <= (light.radius * light.radius); + float3 dist = closest - light.positionVS[0].xyz; + return dot(dist, dist) <= (light.radius * light.radius); } -[numthreads(16, 8, 8)] -void main(uint3 groupId : SV_GroupID, - uint3 dispatchThreadId : SV_DispatchThreadID, - uint3 groupThreadId : SV_GroupThreadID, - uint groupIndex : SV_GroupIndex) -{ - if (all(dispatchThreadId == 0)) - { - lightIndexCounter[0] = 0; - } - - uint visibleLightCount = 0; - uint visibleLightIndices[MAX_CLUSTER_LIGHTS]; - - uint clusterIndex = groupIndex + GROUP_SIZE * groupId.z; - - ClusterAABB cluster = clusters[clusterIndex]; - - uint lightOffset = 0; - uint lightCount, dummy; - lights.GetDimensions(lightCount, dummy); - - while (lightOffset < lightCount) - { - uint batchSize = min(GROUP_SIZE, lightCount - lightOffset); - - if(groupIndex < batchSize) - { - uint lightIndex = lightOffset + groupIndex; - - StructuredLight light = lights[lightIndex]; - - sharedLights[groupIndex] = light; - } - - GroupMemoryBarrierWithGroupSync(); - - for (uint i = 0; i < batchSize; i++) - { - StructuredLight light = lights[i]; - - if (visibleLightCount < MAX_CLUSTER_LIGHTS && LightIntersectsCluster(light, cluster)) - { - visibleLightIndices[visibleLightCount] = lightOffset + i; - visibleLightCount++; - } - } - - lightOffset += batchSize; - } - - GroupMemoryBarrierWithGroupSync(); - - uint offset = 0; - InterlockedAdd(lightIndexCounter[0], visibleLightCount, offset); - - for (uint i = 0; i < visibleLightCount; i++) - { - lightIndexList[offset + i] = visibleLightIndices[i]; - } - - lightGrid[clusterIndex].offset = offset; - lightGrid[clusterIndex].lightCount = visibleLightCount; +[numthreads(16, 8, 8)] void main(uint3 groupId + : SV_GroupID, + uint3 dispatchThreadId + : SV_DispatchThreadID, + uint3 groupThreadId + : SV_GroupThreadID, + uint groupIndex + : SV_GroupIndex) { + if (all(dispatchThreadId == 0)) { + lightIndexCounter[0] = 0; + } + + uint visibleLightCount = 0; + uint visibleLightIndices[MAX_CLUSTER_LIGHTS]; + + uint clusterIndex = groupIndex + GROUP_SIZE * groupId.z; + + ClusterAABB cluster = clusters[clusterIndex]; + + uint lightOffset = 0; + uint lightCount, dummy; + lights.GetDimensions(lightCount, dummy); + + while (lightOffset < lightCount) { + uint batchSize = min(GROUP_SIZE, lightCount - lightOffset); + + if (groupIndex < batchSize) { + uint lightIndex = lightOffset + groupIndex; + + StructuredLight light = lights[lightIndex]; + + sharedLights[groupIndex] = light; + } + + GroupMemoryBarrierWithGroupSync(); + + for (uint i = 0; i < batchSize; i++) { + StructuredLight light = lights[i]; + + if (visibleLightCount < MAX_CLUSTER_LIGHTS && LightIntersectsCluster(light, cluster)) { + visibleLightIndices[visibleLightCount] = lightOffset + i; + visibleLightCount++; + } + } + + lightOffset += batchSize; + } + + GroupMemoryBarrierWithGroupSync(); + + uint offset = 0; + InterlockedAdd(lightIndexCounter[0], visibleLightCount, offset); + + for (uint i = 0; i < visibleLightCount; i++) { + lightIndexList[offset + i] = visibleLightIndices[i]; + } + + lightGrid[clusterIndex].offset = offset; + lightGrid[clusterIndex].lightCount = visibleLightCount; } //https://www.3dgep.com/forward-plus/#Grid_Frustums_Compute_Shader diff --git a/features/Light Limit Fix/Shaders/LightLimitFix/Common.hlsli b/features/Light Limit Fix/Shaders/LightLimitFix/Common.hlsli index c1751740a..655162c95 100644 --- a/features/Light Limit Fix/Shaders/LightLimitFix/Common.hlsli +++ b/features/Light Limit Fix/Shaders/LightLimitFix/Common.hlsli @@ -1,5 +1,5 @@ -#define GROUP_SIZE (16*8*8) +#define GROUP_SIZE (16 * 8 * 8) #define MAX_CLUSTER_LIGHTS 128 #define CLUSTER_BUILDING_DISPATCH_SIZE_X 16 @@ -8,41 +8,48 @@ struct ClusterAABB { - float4 minPoint; - float4 maxPoint; + float4 minPoint; + float4 maxPoint; }; struct LightGrid { - uint offset; - uint lightCount; + uint offset; + uint lightCount; }; struct StructuredLight { - float3 color; - float radius; - float3 positionWS; - float3 positionVS; - uint shadowMode; - uint pad; + float3 color; + float radius; + float3 positionWS[2]; + float3 positionVS[2]; + uint shadowMode; + uint pad; }; cbuffer PerFrame : register(b0) { - row_major float4x4 InvProjMatrix; - float CameraNear; - float CameraFar; - float pad[2]; + row_major float4x4 InvProjMatrix[2]; + float CameraNear; + float CameraFar; + float pad[2]; } float3 GetPositionVS(float2 texcoord, float depth) { - float4 clipSpaceLocation; - clipSpaceLocation.xy = texcoord * 2.0f - 1.0f; - clipSpaceLocation.y *= -1; - clipSpaceLocation.z = depth; - clipSpaceLocation.w = 1.0f; - float4 homogenousLocation = mul(clipSpaceLocation, InvProjMatrix); - return homogenousLocation.xyz / homogenousLocation.w; + float4 clipSpaceLocation; +#ifdef VR + uint eyeIndex = (texcoord.x > .5); + // next code should convert between eyes in VR (may not be necessary) +// texcoord.x = (texcoord.x * 2 - eyeIndex); // [0, 0.5] -> [0, 1], [0.5, 1] -> [0, 1] +#else + uint eyeIndex = 0; +#endif //VR + clipSpaceLocation.xy = texcoord * 2.0f - 1.0f; // convert from [0,1] to [-1,1] + clipSpaceLocation.y *= -1; + clipSpaceLocation.z = depth; + clipSpaceLocation.w = 1.0f; + float4 homogenousLocation = mul(clipSpaceLocation, InvProjMatrix[0]); + return homogenousLocation.xyz / homogenousLocation.w; } diff --git a/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli b/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli index 3384f071d..6aa229b8a 100644 --- a/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli +++ b/features/Light Limit Fix/Shaders/LightLimitFix/LightLimitFix.hlsli @@ -1,68 +1,68 @@ struct LightGrid { - uint offset; - uint lightCount; + uint offset; + uint lightCount; }; struct StructuredLight { - float3 color; - float radius; - float3 positionWS; - float3 positionVS; - uint shadowMode; - uint pad; + float3 color; + float radius; + float3 positionWS[2]; + float3 positionVS[2]; + uint shadowMode; + uint pad; }; struct PerPassLLF { - uint EnableGlobalLights; - float CameraNear; - float CameraFar; - float4 CameraData; - float2 BufferDim; - uint FrameCount; + uint EnableGlobalLights; + float CameraNear; + float CameraFar; + float4 CameraData; + float2 BufferDim; + uint FrameCount; }; -StructuredBuffer lights : register(t17); -StructuredBuffer lightList : register(t18); //MAX_CLUSTER_LIGHTS * 16^3 -StructuredBuffer lightGrid : register(t19); //16^3 +StructuredBuffer lights : register(t17); +StructuredBuffer lightList : register(t18); //MAX_CLUSTER_LIGHTS * 16^3 +StructuredBuffer lightGrid : register(t19); //16^3 #if !defined(SCREEN_SPACE_SHADOWS) Texture2D TexDepthSampler : register(t20); #endif // SCREEN_SPACE_SHADOWS -StructuredBuffer perPassLLF : register(t32); +StructuredBuffer perPassLLF : register(t32); float GetNearPlane() { - return perPassLLF[0].CameraNear; + return perPassLLF[0].CameraNear; } float GetFarPlane() { - return perPassLLF[0].CameraFar; + return perPassLLF[0].CameraFar; } // Get a raw depth from the depth buffer. float GetDepth(float2 uv) { - return TexDepthSampler.Load(int3(uv * perPassLLF[0].BufferDim, 0)); + return TexDepthSampler.Load(int3(uv * perPassLLF[0].BufferDim, 0)); } -bool IsSaturated(float value) { return value == saturate(value); } +bool IsSaturated(float value) { return value == saturate(value); } bool IsSaturated(float2 value) { return IsSaturated(value.x) && IsSaturated(value.y); } // Derived from the interleaved gradient function from Jimenez 2014 http://goo.gl/eomGso float InterleavedGradientNoise(float2 uv) { - // Temporal factor - float frameStep = float(perPassLLF[0].FrameCount % 16) * 0.0625f; - uv.x += frameStep * 4.7526; - uv.y += frameStep * 3.1914; + // Temporal factor + float frameStep = float(perPassLLF[0].FrameCount % 16) * 0.0625f; + uv.x += frameStep * 4.7526; + uv.y += frameStep * 3.1914; - float3 magic = float3(0.06711056f, 0.00583715f, 52.9829189f); - return frac(magic.z * frac(dot(uv, magic.xy))); + float3 magic = float3(0.06711056f, 0.00583715f, 52.9829189f); + return frac(magic.z * frac(dot(uv, magic.xy))); } float GetScreenDepth(float depth) @@ -70,77 +70,74 @@ float GetScreenDepth(float depth) return (perPassLLF[0].CameraData.w / (-depth * perPassLLF[0].CameraData.z + perPassLLF[0].CameraData.x)); } - float GetScreenDepth(float2 uv) { float depth = GetDepth(uv); return GetScreenDepth(depth); } -float ContactShadows(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS) -{ - 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); - - // 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) - saturate(depthDelta / (rayDepth * 0.1)); - } - - return 1.0 - saturate(shadow); +float ContactShadows(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS, 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) - saturate(depthDelta / (rayDepth * 0.1)); + } + + return 1.0 - saturate(shadow); } -float ContactShadowsLong(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS, float radius) -{ - lightDirectionVS *= radius / 32; - - // Offset starting position with interleaved gradient noise - rayPos += lightDirectionVS * offset; - - // Accumulate samples - float shadow = 0.0; - [loop] - for (uint i = 0; i < 32; i++) - { - // Step the ray - rayPos += lightDirectionVS; - float2 rayUV = ViewToUV(rayPos); - - // 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) - saturate(depthDelta / (rayDepth * 0.4)); - } - - return 1.0 - saturate(shadow); +float ContactShadowsLong(float3 rayPos, float2 texcoord, float offset, float3 lightDirectionVS, float radius, uint a_eyeIndex = 0) +{ + lightDirectionVS *= radius / 32; + + // Offset starting position with interleaved gradient noise + rayPos += lightDirectionVS * offset; + + // Accumulate samples + float shadow = 0.0; + [loop] for (uint i = 0; i < 32; 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) - saturate(depthDelta / (rayDepth * 0.4)); + } + + return 1.0 - saturate(shadow); } #if defined(SKINNED) || defined(ENVMAP) || defined(EYE) || defined(MULTI_LAYER_PARALLAX) -#define DRAW_IN_WORLDSPACE +# define DRAW_IN_WORLDSPACE #endif diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 379896a3e..0af8eee42 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -77,7 +77,11 @@ struct VS_OUTPUT float4 Color : COLOR0; float4 FogParam : COLOR1; #if defined(LIGHT_LIMIT_FIX) +# if !defined(VR) row_major float3x4 World[1] : POSITION3; +# else + row_major float3x4 World[2] : POSITION3; +# endif // VR bool WorldSpace : TEXCOORD11; #endif #if defined(VR) @@ -182,7 +186,7 @@ float2 GetTreeShiftVector(float4 position, float4 color) precise float4 tmp4 = (tmp3 * tmp3) * (3.0.xxxx - 2.0.xxxx * tmp3); return (tmp4.xz + 0.1.xx * tmp4.yw) * (TreeParams.z * color.w).xx; } -# endif +# endif // TREE_ANIM # if defined(SKINNED) float3x4 GetBoneMatrix(float4 bones[240], int4 actualIndices, float3 pivot, float4 weights) @@ -439,7 +443,10 @@ VS_OUTPUT main(VS_INPUT input) vsout.FogParam.w = fogColorParam; # if defined(LIGHT_LIMIT_FIX) - vsout.World[eyeIndex] = World[eyeIndex]; + vsout.World[0] = World[0]; +# ifdef VR + vsout.World[1] = World[1]; +# endif // VR # if defined(SKINNED) || defined(ENVMAP) || defined(MULTI_LAYER_PARALLAX) || defined(EYE) vsout.WorldSpace = true; # else @@ -667,11 +674,11 @@ cbuffer PerTechnique : register(b0) cbuffer PerMaterial : register(b1) { - float4 LODTexParams : packoffset(c0); // TerrainTexOffset in xy, LodBlendingEnabled in z + float4 LODTexParams : packoffset(c0); // TerrainTexOffset in xy, LodBlendingEnabled in z float4 TintColor : packoffset(c1); - float4 EnvmapData : packoffset(c2); // fEnvmapScale in x, 1 or 0 in y depending of if has envmask + float4 EnvmapData : packoffset(c2); // fEnvmapScale in x, 1 or 0 in y depending of if has envmask float4 ParallaxOccData : packoffset(c3); - float4 SpecularColor : packoffset(c4); // Shininess in w, color in xyz + float4 SpecularColor : packoffset(c4); // Shininess in w, color in xyz float4 SparkleParams : packoffset(c5); float4 MultiLayerParallaxData : packoffset(c6); // Layer thickness in x, refraction scale in y, uv scale in zw float4 LightingEffectParams : packoffset(c7); // fSubSurfaceLightRolloff in x, fRimLightPower in y @@ -698,7 +705,7 @@ cbuffer PerGeometry : register(b2) float4 SSRParams : packoffset(c7); float4 WorldMapOverlayParametersPS : packoffset(c8); float4 ProjectedUVParams2 : packoffset(c9); - float4 ProjectedUVParams3 : packoffset(c10); // fProjectedUVDiffuseNormalTilingScale in x, fProjectedUVNormalDetailTilingScale in y, EnableProjectedNormals in w + float4 ProjectedUVParams3 : packoffset(c10); // fProjectedUVDiffuseNormalTilingScale in x, fProjectedUVNormalDetailTilingScale in y, EnableProjectedNormals in w row_major float3x4 DirectionalAmbient : packoffset(c11); float4 AmbientSpecularTintAndFresnelPower : packoffset(c14); // Fresnel power in z, color in xyz float4 PointLightPosition[7] : packoffset(c15); // point light radius in w @@ -717,7 +724,7 @@ cbuffer PerGeometry : register(b2) float4 SSRParams : packoffset(c19); float4 WorldMapOverlayParametersPS : packoffset(c20); float4 ProjectedUVParams2 : packoffset(c21); - float4 ProjectedUVParams3 : packoffset(c22); // fProjectedUVDiffuseNormalTilingScale in x, fProjectedUVNormalDetailTilingScale in y, EnableProjectedNormals in w + float4 ProjectedUVParams3 : packoffset(c22); // fProjectedUVDiffuseNormalTilingScale in x, fProjectedUVNormalDetailTilingScale in y, EnableProjectedNormals in w row_major float3x4 DirectionalAmbient : packoffset(c23); float4 AmbientSpecularTintAndFresnelPower : packoffset(c26); // Fresnel power in z, color in xyz float4 PointLightPosition[14] : packoffset(c27); // point light radius in w @@ -1059,7 +1066,8 @@ float GetSnowParameterY(float texProjTmp, float alpha) # include "LightLimitFix/LightLimitFix.hlsli" # endif -PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) +PS_OUTPUT main(PS_INPUT input, bool frontFace + : SV_IsFrontFace) { PS_OUTPUT psout; @@ -1676,13 +1684,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif # if defined(LIGHT_LIMIT_FIX) - float2 screenUV = ViewToUV(viewPosition); + float2 screenUV = ViewToUV(viewPosition, true, eyeIndex); float screenNoise = InterleavedGradientNoise(screenUV * perPassLLF[0].BufferDim); # endif if (numLights > 0) { - [loop] - for (float lightIndex = 0; lightIndex < numLights; ++lightIndex) { + [loop] for (float lightIndex = 0; lightIndex < numLights; ++lightIndex) + { # if defined(DEFSHADOW) float shadowComponent; if (lightIndex < numShadowLights) { @@ -1696,7 +1704,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float lightDist = length(lightDirection); float intensityFactor = saturate(lightDist / PointLightPosition[intLightIndex].w); if (intensityFactor == 1) - continue; + continue; float intensityMultiplier = 1 - intensityFactor * intensityFactor; float3 lightColor = PointLightColor[intLightIndex].xyz; @@ -1758,31 +1766,30 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } float3 screenSpaceNormal; - screenSpaceNormal.x = dot(input.ScreenNormalTransform0.xyz, normal.xyz); - screenSpaceNormal.y = dot(input.ScreenNormalTransform1.xyz, normal.xyz); - screenSpaceNormal.z = dot(input.ScreenNormalTransform2.xyz, normal.xyz); - screenSpaceNormal = normalize(screenSpaceNormal); - -#if defined(LIGHT_LIMIT_FIX) - if (perPassLLF[0].EnableGlobalLights){ + screenSpaceNormal.x = dot(input.ScreenNormalTransform0.xyz, normal.xyz); + screenSpaceNormal.y = dot(input.ScreenNormalTransform1.xyz, normal.xyz); + screenSpaceNormal.z = dot(input.ScreenNormalTransform2.xyz, normal.xyz); + screenSpaceNormal = normalize(screenSpaceNormal); + +# if defined(LIGHT_LIMIT_FIX) + if (perPassLLF[0].EnableGlobalLights) { float clampedDepth = clamp(viewPosition.z, GetNearPlane(), GetFarPlane()); - - uint clusterZ = uint(max((log2(clampedDepth) - log2(GetNearPlane())) * 24.0 / log2(GetFarPlane() / GetNearPlane()), 0.0)); + + uint clusterZ = uint(max((log2(clampedDepth) - log2(GetNearPlane())) * 24.0 / log2(GetFarPlane() / GetNearPlane()), 0.0)); uint2 clusterDim = ceil(perPassLLF[0].BufferDim / float2(16, 8)); uint3 cluster = uint3(uint2(input.Position.xy / clusterDim), clusterZ); - uint clusterIndex = cluster.x + (16 * cluster.y) + (16 * 8 * cluster.z); - + uint clusterIndex = cluster.x + (16 * cluster.y) + (16 * 8 * cluster.z); + uint lightCount = lightGrid[clusterIndex].lightCount; - if (lightCount > 0){ + if (lightCount > 0) { uint lightOffset = lightGrid[clusterIndex].offset; - float3 worldSpaceNormal = normalize(mul(CameraViewInverse[0], float4(screenSpaceNormal, 0))); - float3 worldSpaceViewDirection = -normalize(input.WorldPosition.xyz); + float3 worldSpaceNormal = normalize(mul(CameraViewInverse[eyeIndex], float4(screenSpaceNormal, 0))); + float3 worldSpaceViewDirection = -normalize(input.WorldPosition.xyz); -# if (defined(SKINNED) || !defined(MODELSPACENORMALS)) && !defined(DRAW_IN_WORLDSPACE) - if (!input.WorldSpace) - { +# if (defined(SKINNED) || !defined(MODELSPACENORMALS)) && !defined(DRAW_IN_WORLDSPACE) + if (!input.WorldSpace) { input.TBN0.xyz = mul(tbn, input.World[eyeIndex][0].xyz); input.TBN1.xyz = mul(tbn, input.World[eyeIndex][1].xyz); input.TBN2.xyz = mul(tbn, input.World[eyeIndex][2].xyz); @@ -1796,72 +1803,71 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) input.TBN2.xyz = tempTbnTr[2]; tbn = float3x3(input.TBN0.xyz, input.TBN1.xyz, input.TBN2.xyz); } -# endif - [loop] - for (uint i = 0; i < lightCount; i++) - { +# endif + [loop] for (uint i = 0; i < lightCount; i++) + { uint light_index = lightList[lightOffset + i]; StructuredLight light = lights[light_index]; - float3 lightDirection = light.positionWS.xyz - input.WorldPosition.xyz; + float3 lightDirection = light.positionWS[eyeIndex].xyz - input.WorldPosition.xyz; float lightDist = length(lightDirection); float intensityFactor = saturate(lightDist / light.radius); if (intensityFactor == 1) continue; - float intensityMultiplier = 1 - intensityFactor * intensityFactor; + float intensityMultiplier = 1 - intensityFactor * intensityFactor; float3 lightColor = light.color.xyz; float3 nsLightColor = lightColor; float3 normalizedLightDirection = normalize(lightDirection); - if (!FrameParams.z && FrameParams.y && light.shadowMode){ - float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection); + if (!FrameParams.z && FrameParams.y && light.shadowMode) { + float3 normalizedLightDirectionVS = WorldToView(normalizedLightDirection, true, eyeIndex); if (light.shadowMode == 2) - lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS); + lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, eyeIndex); else - lightColor *= ContactShadowsLong(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius); + lightColor *= ContactShadowsLong(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, light.radius, eyeIndex); } - - # if defined(CPM_AVAILABLE) + +# if defined(CPM_AVAILABLE) if (perPassParallax[0].EnableShadows) { float3 lightDirectionTS = mul(normalizedLightDirection, tbn).xyz; - # if defined(PARALLAX) +# if defined(PARALLAX) if (perPassParallax[0].EnableParallax) lightColor *= GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality); - # elif defined(LANDSCAPE) +# elif defined(LANDSCAPE) if (perPassParallax[0].EnableTerrainParallax) lightColor *= GetParallaxSoftShadowMultiplierTerrain(input, terrainUVs, mipLevel, lightDirectionTS, sh0, parallaxShadowQuality); - # elif defined(ENVMAP) +# elif defined(ENVMAP) if (complexMaterialParallax) lightColor *= GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, parallaxShadowQuality); - # endif +# endif } - # endif - - #if defined(PBR) +# endif + +# if defined(PBR) totalRadiance += GetLightRadiance(worldSpaceNormal.xyz, normalizedLightDirection, worldSpaceViewDirection, F0, lightColor * intensityMultiplier.xxx, baseColor.xyz, roughness, metallic); - #else +# else float lightAngle = dot(worldSpaceNormal.xyz, normalizedLightDirection.xyz); float3 lightDiffuseColor = lightColor * saturate(lightAngle.xxx); - - #if defined (SOFT_LIGHTING) + +# if defined(SOFT_LIGHTING) lightDiffuseColor += nsLightColor * GetSoftLightMultiplier(dot(worldSpaceNormal.xyz, lightDirection.xyz)) * rimSoftLightColor.xyz; - #endif - - #if defined (RIM_LIGHTING) +# endif + +# if defined(RIM_LIGHTING) lightDiffuseColor += nsLightColor * GetRimLightMultiplier(normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz) * rimSoftLightColor.xyz; - #endif +# endif - #if defined (BACK_LIGHTING) +# if defined(BACK_LIGHTING) lightDiffuseColor += (saturate(-lightAngle) * backLightColor.xyz) * nsLightColor; - #endif - - #if defined (SPECULAR) || (defined (SPARKLE) && !defined(SNOW)) +# endif + +# if defined(SPECULAR) || (defined(SPARKLE) && !defined(SNOW)) lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz, lightColor, shininess, uv) * intensityMultiplier.xxx; - #endif +# endif lightsDiffuseColor += lightDiffuseColor * intensityMultiplier.xxx; - #endif +# endif } } } @@ -1912,7 +1918,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float4 color; color.xyz = diffuseColor * baseColor.xyz; -# endif // PBR +# endif // PBR # if defined(HAIR) float3 vertexColor = (input.Color.yyy * (TintColor.xyz - 1.0.xxx) + 1.0.xxx) * color.xyz; @@ -1990,7 +1996,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) color.xyz = min(specularTmp.xyz, color.xyz); # endif // defined (SPECULAR) || defined(AMBIENT_SPECULAR) || defined(SPARKLE) -# endif // !defined(PBR) +# endif // !defined(PBR) # if defined(LANDSCAPE) && !defined(LOD_LAND_BLEND) psout.Albedo.w = 0; @@ -2044,7 +2050,24 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif +// manually define SHOW_GRIDS to see the grids +// # define SHOW_GRIDS +# if defined(LIGHT_LIMIT_FIX) && defined(SHOW_GRIDS) + float clampedDepth = clamp(viewPosition.z, GetNearPlane(), GetFarPlane()); + uint clusterZ = uint(max((log2(clampedDepth) - log2(GetNearPlane())) * 24.0 / log2(GetFarPlane() / GetNearPlane()), 0.0)); + uint2 clusterDim = ceil(perPassLLF[0].BufferDim / float2(16, 8)); + uint3 cluster = uint3(uint2(input.Position.xy / clusterDim), clusterZ); + uint clusterIndex = cluster.x + (16 * cluster.y) + (16 * 8 * cluster.z); + float level = (float)clusterIndex; + float3 col; + col.r = sin(level); + col.g = sin(level * 2); + col.b = cos(level); + + psout.Albedo.xyz = col; +# else psout.Albedo.xyz = color.xyz - tmpColor.xyz * FrameParams.zzz; +# endif // defined(LIGHT_LIMIT_FIX) && defined(SHOW_GRIDS) # if defined(SNOW) psout.SnowParameters.x = dot(lightsSpecularColor, float3(0.3, 0.59, 0.11)); diff --git a/src/Feature.cpp b/src/Feature.cpp index 43ff9cb3d..db10063e1 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -83,6 +83,7 @@ const std::vector& Feature::GetFeatureList() static std::vector featuresVR = { GrassLighting::GetSingleton(), ExtendedMaterials::GetSingleton(), + LightLimitFix::GetSingleton() }; return REL::Module::IsVR() ? featuresVR : features; diff --git a/src/Features/GrassLighting.cpp b/src/Features/GrassLighting.cpp index 47d8edebd..699b677a1 100644 --- a/src/Features/GrassLighting.cpp +++ b/src/Features/GrassLighting.cpp @@ -62,25 +62,17 @@ void GrassLighting::ModifyGrass(const RE::BSShader*, const uint32_t descriptor) RE::NiTransform& dalcTransform = state.directionalAmbientTransform; auto imageSpaceManager = RE::ImageSpaceManager::GetSingleton(); - if (REL::Module::IsVR()) { - PerFrameVR perFrameDataVR{}; - ZeroMemory(&perFrameDataVR, sizeof(perFrameDataVR)); - Util::StoreTransform3x4NoScale(perFrameDataVR.DirectionalAmbient, dalcTransform); - - perFrameDataVR.SunlightScale = imageSpaceManager->data.baseData.cinematic.brightness; - perFrameDataVR.Settings = settings; + PerFrame perFrameData{}; + ZeroMemory(&perFrameData, sizeof(perFrameData)); + Util::StoreTransform3x4NoScale(perFrameData.DirectionalAmbient, dalcTransform); - perFrame->Update(perFrameDataVR); + if (REL::Module::IsVR()) { + perFrameData.SunlightScale = imageSpaceManager->data.baseData.cinematic.brightness; } else { - PerFrame perFrameData{}; - ZeroMemory(&perFrameData, sizeof(perFrameData)); - Util::StoreTransform3x4NoScale(perFrameData.DirectionalAmbient, dalcTransform); - perFrameData.SunlightScale = imageSpaceManager->data.baseData.hdr.sunlightScale; - perFrameData.Settings = settings; - - perFrame->Update(perFrameData); } + perFrameData.Settings = settings; + perFrame->Update(perFrameData); updatePerFrame = false; } @@ -119,7 +111,7 @@ void GrassLighting::Save(json& o_json) void GrassLighting::SetupResources() { - perFrame = !REL::Module::IsVR() ? new ConstantBuffer(ConstantBufferDesc()) : new ConstantBuffer(ConstantBufferDesc()); + perFrame = new ConstantBuffer(ConstantBufferDesc()); } void GrassLighting::Reset() diff --git a/src/Features/GrassLighting.h b/src/Features/GrassLighting.h index ff5fee951..2e92e1227 100644 --- a/src/Features/GrassLighting.h +++ b/src/Features/GrassLighting.h @@ -32,14 +32,6 @@ struct GrassLighting : Feature float pad[1]; }; - struct alignas(16) PerFrameVR - { - DirectX::XMFLOAT3X4 DirectionalAmbient; - float SunlightScale; - Settings Settings; - float pad[1]; - }; - Settings settings; bool updatePerFrame = false; diff --git a/src/Features/LightLimitFix.cpp b/src/Features/LightLimitFix.cpp index 765e80b78..25f3c03fa 100644 --- a/src/Features/LightLimitFix.cpp +++ b/src/Features/LightLimitFix.cpp @@ -54,6 +54,7 @@ void LightLimitFix::DrawSettings() if (ImGui::TreeNodeEx("Statistics", ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::Text(std::format("Clustered Light Count : {}", lightCount).c_str()); ImGui::Text(std::format("Particle Lights Detection Count : {}", particleLightsDetectionHits).c_str()); + ImGui::Text(std::format("Strict Lights Count : {}", strictLightsCount).c_str()); ImGui::TreePop(); } @@ -190,10 +191,26 @@ void LightLimitFix::BSLightingShader_SetupGeometry_Before(RE::BSRenderPass*) strictLightsData.clear(); } -void LightLimitFix::BSLightingShader_SetupGeometry_GeometrySetupConstantPointLights(RE::BSRenderPass* Pass, uint32_t NumLights, uint32_t ShadowLightCount) +void LightLimitFix::SetLightPosition(LightLimitFix::LightData& a_light, RE::NiPoint3& a_initialPosition) { auto state = RE::BSGraphics::RendererShadowState::GetSingleton(); + for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) { + auto eyePosition = eyeCount == 1 ? + state->GetRuntimeData().posAdjust.getEye(eyeIndex) : + state->GetVRRuntimeData().posAdjust.getEye(eyeIndex); + auto viewMatrix = eyeCount == 1 ? + state->GetRuntimeData().cameraData.getEye(eyeIndex).viewMat : + state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; + auto worldPos = a_initialPosition - eyePosition; + a_light.positionWS[eyeIndex].x = worldPos.x; + a_light.positionWS[eyeIndex].y = worldPos.y; + a_light.positionWS[eyeIndex].z = worldPos.z; + a_light.positionVS[eyeIndex] = DirectX::SimpleMath::Vector3::Transform(a_light.positionWS[eyeIndex], viewMatrix); + } +} +void LightLimitFix::BSLightingShader_SetupGeometry_GeometrySetupConstantPointLights(RE::BSRenderPass* Pass, uint32_t NumLights, uint32_t ShadowLightCount) +{ NumLights = (uint8_t)std::min(7, Pass->numLights - 1); for (uint8_t i = 0; i < NumLights; i++) { @@ -209,11 +226,7 @@ void LightLimitFix::BSLightingShader_SetupGeometry_GeometrySetupConstantPointLig light.color.z = niLight->GetLightRuntimeData().diffuse.blue * dimmer; light.radius = niLight->GetLightRuntimeData().radius.x; - auto worldPos = niLight->world.translate - state->posAdjust.getEye(); - light.positionWS.x = worldPos.x; - light.positionWS.y = worldPos.y; - light.positionWS.z = worldPos.z; - light.positionVS = DirectX::SimpleMath::Vector3::Transform(light.positionWS, state->cameraData.getEye().viewMat); + SetLightPosition(light, niLight->world.translate); //light.shadow = false; //light.mask = -1; @@ -249,7 +262,7 @@ void LightLimitFix::AddParticleLightLuminance(RE::NiPoint3& targetPosition, int& std::lock_guard lk{ cachedParticleLightsMutex }; particleLightsDetectionHits = 0; if (settings.EnableParticleLightsDetection) { - for (auto& light : cachedParticleLights) { + for (auto& light : cachedParticleLights[0]) { // left eye only auto luminance = CalculateLuminance(light, targetPosition); lightLevel += luminance; if (luminance > 0.0) @@ -300,7 +313,11 @@ void LightLimitFix::Bind() auto context = RE::BSGraphics::Renderer::GetSingleton()->GetRuntimeData().context; auto accumulator = RE::BSGraphics::BSShaderAccumulator::GetCurrentAccumulator(); - if (RE::BSGraphics::RendererShadowState::GetSingleton()->GetRuntimeData().cubeMapRenderTarget == RE::RENDER_TARGETS_CUBEMAP::kREFLECTIONS || accumulator->activeShadowSceneNode != RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]) { + auto reflections = (!REL::Module::IsVR() ? + RE::BSGraphics::RendererShadowState::GetSingleton()->GetRuntimeData().cubeMapRenderTarget : + RE::BSGraphics::RendererShadowState::GetSingleton()->GetVRRuntimeData().cubeMapRenderTarget) == RE::RENDER_TARGETS_CUBEMAP::kREFLECTIONS; + + if (reflections || accumulator->GetRuntimeData().activeShadowSceneNode != RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]) { PerPass perPassData{}; perPassData.EnableGlobalLights = false; @@ -326,10 +343,10 @@ void LightLimitFix::Bind() { PerPass perPassData{}; - perPassData.CameraData.x = accumulator->kCamera->viewFrustum.fFar; - perPassData.CameraData.y = accumulator->kCamera->viewFrustum.fNear; - perPassData.CameraData.z = accumulator->kCamera->viewFrustum.fFar - accumulator->kCamera->viewFrustum.fNear; - perPassData.CameraData.w = accumulator->kCamera->viewFrustum.fFar * accumulator->kCamera->viewFrustum.fNear; + perPassData.CameraData.x = accumulator->kCamera->GetRuntimeData2().viewFrustum.fFar; + perPassData.CameraData.y = accumulator->kCamera->GetRuntimeData2().viewFrustum.fNear; + perPassData.CameraData.z = accumulator->kCamera->GetRuntimeData2().viewFrustum.fFar - accumulator->kCamera->GetRuntimeData2().viewFrustum.fNear; + 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; @@ -505,6 +522,75 @@ void LightLimitFix::Draw(const RE::BSShader* shader, const uint32_t descriptor) } } +float LightLimitFix::CalculateLightDistance(float3 a_lightPosition, float a_radius) +{ + return (a_lightPosition.x * a_lightPosition.x) + (a_lightPosition.y * a_lightPosition.y) + (a_lightPosition.z * a_lightPosition.z) - (a_radius * a_radius); +} + +bool LightLimitFix::AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, float dimmerMult, int a_eyeIndex, RE::BSGeometry* a_geometry, double timer) +{ + static float& lightFadeStart = (*(float*)RELOCATION_ID(527668, 414582).address()); + static float& lightFadeEnd = (*(float*)RELOCATION_ID(527669, 414583).address()); + + float distance = CalculateLightDistance(light.positionWS[a_eyeIndex], light.radius); + + float dimmer = 0.0f; + if (!settings.EnableParticleLightsFade || distance < lightFadeStart || lightFadeEnd == 0.0f) { + dimmer = 1.0f; + } else if (distance <= lightFadeEnd) { + dimmer = 1.0f - ((distance - lightFadeStart) / (lightFadeEnd - lightFadeStart)); + } else { + dimmer = 0.0f; + } + auto state = RE::BSGraphics::RendererShadowState::GetSingleton(); + if (dimmer != 0) { + if ((light.color.x > 0 || light.color.y > 0 || light.color.z > 0) && light.radius > 0) { + if (a_geometry) { + auto seed = (std::uint32_t)std::hash{}(a_geometry); + + siv::PerlinNoise perlin1{ seed }; + siv::PerlinNoise perlin2{ seed + 1 }; + siv::PerlinNoise perlin3{ seed + 2 }; + siv::PerlinNoise perlin4{ seed + 3 }; + + light.positionWS[0].x += (float)perlin1.noise1D(timer) * 5.0f; + light.positionWS[0].y += (float)perlin2.noise1D(timer) * 5.0f; + light.positionWS[0].z += (float)perlin3.noise1D(timer) * 5.0f; + light.positionWS[1] = light.positionWS[0]; + dimmer = std::max(0.0f, dimmer - ((float)perlin4.noise1D_01(timer) * 0.5f)); + } + CachedParticleLight cachedParticleLight{}; + cachedParticleLight.color = { light.color.x, light.color.y, light.color.z }; + cachedParticleLight.radius = light.radius; + + light.color.x *= dimmer * dimmerMult; + light.color.y *= dimmer * dimmerMult; + light.color.z *= dimmer * dimmerMult; + + light.shadowMode = 2 * settings.EnableContactShadows; + + auto eyePosition = eyeCount == 1 ? + state->GetRuntimeData().posAdjust.getEye(a_eyeIndex) : + state->GetVRRuntimeData().posAdjust.getEye(a_eyeIndex); + cachedParticleLight.position = { light.positionWS[a_eyeIndex].x + eyePosition.x, light.positionWS[a_eyeIndex].y + eyePosition.y, light.positionWS[a_eyeIndex].z + eyePosition.z }; + for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) { + auto viewMatrix = eyeCount == 1 ? + state->GetRuntimeData().cameraData.getEye(eyeIndex).viewMat : + state->GetVRRuntimeData().cameraData.getEye(eyeIndex).viewMat; + light.positionVS[eyeIndex] = DirectX::SimpleMath::Vector3::Transform(light.positionWS[eyeIndex], viewMatrix); + } + for (int eyeIndex = 0; eyeIndex < eyeCount; eyeIndex++) { + cachedParticleLights[eyeIndex].push_back(cachedParticleLight); + logger::debug("Adding cachedParticleLight[{}] {:x} color {} position {}", eyeIndex, reinterpret_cast(&cachedParticleLight), cachedParticleLight.color, cachedParticleLight.position); + } + lightsData.push_back(light); + logger::debug("Adding light {:x} at eyeIndex {} WS {} {} VS {} {}", reinterpret_cast(&light), a_eyeIndex, light.positionWS[0], light.positionWS[1], light.positionVS[0], light.positionVS[1]); + return true; + } + } + return false; +} + void LightLimitFix::UpdateLights() { std::uint32_t currentLightCount = 0; // Max number of lights is 4294967295 @@ -521,8 +607,8 @@ void LightLimitFix::UpdateLights() if (auto playerCamera = RE::PlayerCamera::GetSingleton()) { if (playerCamera->IsInFirstPerson()) { if (auto player = RE::PlayerCharacter::GetSingleton()) { - firstPersonLight = player->GetPlayerRuntimeData().firstPersonLight.get(); - thirdPersonLight = player->GetPlayerRuntimeData().thirdPersonLight.get(); + firstPersonLight = player->GetInfoRuntimeData().firstPersonLight.get(); + thirdPersonLight = player->GetInfoRuntimeData().thirdPersonLight.get(); if (auto light = player->extraList.GetByType()) { refLight = light->lightData->light.get(); } @@ -541,6 +627,7 @@ void LightLimitFix::UpdateLights() if (!RE::UI::GetSingleton()->GameIsPaused()) timer += *g_deltaTime; + //process point lights for (auto& e : shadowSceneNode->GetRuntimeData().activePointLights) { if (auto bsLight = e.get()) { if (auto niLight = bsLight->light.get()) { @@ -554,11 +641,7 @@ void LightLimitFix::UpdateLights() light.color.z = niLight->GetLightRuntimeData().diffuse.blue * dimmer; light.radius = niLight->GetLightRuntimeData().radius.x; - auto worldPos = niLight->world.translate - state->posAdjust.getEye(); - light.positionWS.x = worldPos.x; - light.positionWS.y = worldPos.y; - light.positionWS.z = worldPos.z; - light.positionVS = DirectX::SimpleMath::Vector3::Transform(light.positionWS, state->cameraData.getEye().viewMat); + SetLightPosition(light, niLight->world.translate); light.shadowMode = bsLight == firstPersonLight || bsLight == thirdPersonLight || niLight == refLight || niLight == magicLight; @@ -570,15 +653,18 @@ void LightLimitFix::UpdateLights() } } - static float& lightFadeStart = (*(float*)RELOCATION_ID(527668, 414582).address()); - static float& lightFadeEnd = (*(float*)RELOCATION_ID(527669, 414583).address()); - { std::lock_guard lk{ cachedParticleLightsMutex }; - cachedParticleLights.clear(); - + cachedParticleLights[0].clear(); + cachedParticleLights[1].clear(); for (const auto& particleLight : particleLights) { - if (const auto particleSystem = netimmerse_cast(particleLight.first)) { + auto eyeIndex = 0; // only calculate for left eye + auto eyePosition = eyeCount == 1 ? + state->GetRuntimeData().posAdjust.getEye(eyeIndex) : + state->GetVRRuntimeData().posAdjust.getEye(eyeIndex); + // process BSGeometry + if (const auto particleSystem = netimmerse_cast(particleLight.first); + particleSystem && particleSystem->particleData.get()) { auto particleData = particleSystem->particleData.get(); LightData light{}; uint32_t clusteredLights = 0; @@ -590,163 +676,59 @@ void LightLimitFix::UpdateLights() float radius = particleData->sizes[p] * settings.ParticleLightsRadius; - RE::NiPoint3 positionWS = particleData->positions[p] + (particleSystem->isWorldspace ? RE::NiPoint3{} : (particleLight.first->worldBound.center)) - state->posAdjust.getEye(); + auto initialPosition = particleData->positions[p] + (particleSystem->isWorldspace ? RE::NiPoint3{} : (particleLight.first->worldBound.center)); + + RE::NiPoint3 positionWS = initialPosition - eyePosition; if (clusteredLights) { float radiusDiff = abs((light.radius / (float)clusteredLights) - radius); radiusDiff *= radiusDiff; - auto averagePosition = light.positionWS / (float)clusteredLights; + auto averagePosition = light.positionWS[eyeIndex] / (float)clusteredLights; float positionDiff = positionWS.GetDistance({ averagePosition.x, averagePosition.y, averagePosition.z }); if ((radiusDiff + positionDiff) > settings.ParticleLightsOptimisationClusterRadius || !settings.EnableParticleLightsOptimization) { light.radius /= (float)clusteredLights; - light.positionWS /= (float)clusteredLights; - - float distance = (light.positionWS.x * light.positionWS.x) + (light.positionWS.y * light.positionWS.y) + (light.positionWS.z * light.positionWS.z) - (light.radius * light.radius); - - float dimmer = 0.0f; - if (!settings.EnableParticleLightsFade || distance < lightFadeStart || lightFadeEnd == 0.0f) { - dimmer = 1.0f; - } else if (distance <= lightFadeEnd) { - dimmer = 1.0f - ((distance - lightFadeStart) / (lightFadeEnd - lightFadeStart)); - } else { - dimmer = 0.0f; - } - - if (dimmer != 0) { - if ((light.color.x > 0 || light.color.y > 0 || light.color.z > 0) && light.radius > 0) { - CachedParticleLight cachedParticleLight{}; - cachedParticleLight.color = { light.color.x, light.color.y, light.color.z }; - cachedParticleLight.position = { light.positionWS.x + state->posAdjust.getEye().x, light.positionWS.y + state->posAdjust.getEye().y, light.positionWS.z + state->posAdjust.getEye().z }; - cachedParticleLight.radius = light.radius; - cachedParticleLights.push_back(cachedParticleLight); + light.positionWS[eyeIndex] /= (float)clusteredLights; - light.color.x *= dimmer * 2.0f; - light.color.y *= dimmer * 2.0f; - light.color.z *= dimmer * 2.0f; - - light.positionVS = DirectX::SimpleMath::Vector3::Transform(light.positionWS, state->cameraData.getEye().viewMat); - - light.shadowMode = 2 * settings.EnableContactShadows; - - lightsData.push_back(light); - currentLightCount++; - } - } + currentLightCount += AddCachedParticleLights(lightsData, light, 2.0f, eyeIndex); clusteredLights = 0; light.color = { 0, 0, 0 }; light.radius = 0; - light.positionWS = { 0, 0, 0 }; + light.positionWS[eyeIndex] = { 0, 0, 0 }; } } clusteredLights++; light.radius += radius; - light.positionWS.x += positionWS.x; - light.positionWS.y += positionWS.y; - light.positionWS.z += positionWS.z; + light.positionWS[eyeIndex].x += positionWS.x; + light.positionWS[eyeIndex].y += positionWS.y; + light.positionWS[eyeIndex].z += positionWS.z; } if (clusteredLights) { light.radius /= (float)clusteredLights; - light.positionWS /= (float)clusteredLights; + light.positionWS[eyeIndex] /= (float)clusteredLights; - float distance = (light.positionWS.x * light.positionWS.x) + (light.positionWS.y * light.positionWS.y) + (light.positionWS.z * light.positionWS.z) - (light.radius * light.radius); - - float dimmer = 0.0f; - if (!settings.EnableParticleLightsFade || distance < lightFadeStart || lightFadeEnd == 0.0f) { - dimmer = 1.0f; - } else if (distance <= lightFadeEnd) { - dimmer = 1.0f - ((distance - lightFadeStart) / (lightFadeEnd - lightFadeStart)); - } else { - dimmer = 0.0f; - } - - if (dimmer != 0) { - if ((light.color.x > 0 || light.color.y > 0 || light.color.z > 0) && light.radius > 0) { - CachedParticleLight cachedParticleLight{}; - cachedParticleLight.color = { light.color.x, light.color.y, light.color.z }; - cachedParticleLight.position = { light.positionWS.x + state->posAdjust.getEye().x, light.positionWS.y + state->posAdjust.getEye().y, light.positionWS.z + state->posAdjust.getEye().z }; - cachedParticleLight.radius = light.radius; - cachedParticleLights.push_back(cachedParticleLight); - - light.color.x *= dimmer * 2.0f; - light.color.y *= dimmer * 2.0f; - light.color.z *= dimmer * 2.0f; - - light.positionVS = DirectX::SimpleMath::Vector3::Transform(light.positionWS, state->cameraData.getEye().viewMat); - - light.shadowMode = 2 * settings.EnableContactShadows; - - lightsData.push_back(light); - currentLightCount++; - } - } + currentLightCount += AddCachedParticleLights(lightsData, light, 2.0f, eyeIndex); } + } else { + // process NiColor LightData light{}; light.color.x = particleLight.second.red; light.color.y = particleLight.second.green; light.color.z = particleLight.second.blue; - float radius = particleLight.first->modelBound.radius * particleLight.first->world.scale; + float radius = particleLight.first->GetModelData().modelBound.radius * particleLight.first->world.scale; light.radius = radius * settings.ParticleLightsRadiusBillboards; - RE::NiPoint3 positionWS = particleLight.first->worldBound.center - state->posAdjust.getEye(); - light.positionWS.x = positionWS.x; - light.positionWS.y = positionWS.y; - light.positionWS.z = positionWS.z; - - float distance = (light.positionWS.x * light.positionWS.x) + (light.positionWS.y * light.positionWS.y) + (light.positionWS.z * light.positionWS.z) - (light.radius * light.radius); + SetLightPosition(light, particleLight.first->worldBound.center); //light is complete for both eyes by now - float dimmer = 0.0f; - if (!settings.EnableParticleLightsFade || distance < lightFadeStart || lightFadeEnd == 0.0f) { - dimmer = 1.0f; - } else if (distance <= lightFadeEnd) { - dimmer = 1.0f - ((distance - lightFadeStart) / (lightFadeEnd - lightFadeStart)); - } else { - dimmer = 0.0f; - } - - if (dimmer != 0) { - if ((light.color.x > 0 || light.color.y > 0 || light.color.z > 0) && light.radius > 0) { - { - auto seed = (std::uint32_t)std::hash{}(particleLight.first); - - siv::PerlinNoise perlin1{ seed }; - siv::PerlinNoise perlin2{ seed + 1 }; - siv::PerlinNoise perlin3{ seed + 2 }; - siv::PerlinNoise perlin4{ seed + 3 }; - - light.positionWS.x += (float)perlin1.noise1D(timer) * 5.0f; - light.positionWS.y += (float)perlin2.noise1D(timer) * 5.0f; - light.positionWS.z += (float)perlin3.noise1D(timer) * 5.0f; - - dimmer = std::max(0.0f, dimmer - ((float)perlin4.noise1D_01(timer) * 0.5f)); - } - - CachedParticleLight cachedParticleLight{}; - cachedParticleLight.color = { light.color.x, light.color.y, light.color.z }; - cachedParticleLight.position = { light.positionWS.x + state->posAdjust.getEye().x, light.positionWS.y + state->posAdjust.getEye().y, light.positionWS.z + state->posAdjust.getEye().z }; - cachedParticleLight.radius = light.radius; - cachedParticleLights.push_back(cachedParticleLight); - - light.color.x *= dimmer; - light.color.y *= dimmer; - light.color.z *= dimmer; - - light.positionVS = DirectX::SimpleMath::Vector3::Transform(light.positionWS, state->cameraData.getEye().viewMat); - - light.shadowMode = 2 * settings.EnableContactShadows; - - lightsData.push_back(light); - currentLightCount++; - } - } + currentLightCount += AddCachedParticleLights(lightsData, light, 1.0f, eyeIndex, particleLight.first, timer); } } } @@ -792,15 +774,19 @@ void LightLimitFix::UpdateLights() { auto accumulator = RE::BSGraphics::BSShaderAccumulator::GetCurrentAccumulator(); - lightsNear = std::max(0.0f, accumulator->kCamera->viewFrustum.fNear); - lightsFar = std::min(16384.0f, accumulator->kCamera->viewFrustum.fFar); - - float fov = atan(1.0f / static_cast(state->cameraData.getEye().projMatrixUnjittered).m[0][0]) * 2.0f * (180.0f / 3.14159265359f); + lightsNear = std::max(0.0f, accumulator->kCamera->GetRuntimeData2().viewFrustum.fNear); + lightsFar = std::min(16384.0f, accumulator->kCamera->GetRuntimeData2().viewFrustum.fFar); + auto projMatrixUnjittered = eyeCount == 1 ? state->GetRuntimeData().cameraData.getEye().projMatrixUnjittered : state->GetVRRuntimeData().cameraData.getEye().projMatrixUnjittered; + float fov = atan(1.0f / static_cast(projMatrixUnjittered).m[0][0]) * 2.0f * (180.0f / 3.14159265359f); static float _near = 0.0f, _far = 0.0f, _fov = 0.0f, _lightsNear = 0.0f, _lightsFar = 0.0f; - if (fabs(_near - accumulator->kCamera->viewFrustum.fNear) > 1e-4 || fabs(_far - accumulator->kCamera->viewFrustum.fFar) > 1e-4 || fabs(_fov - fov) > 1e-4 || fabs(_lightsNear - lightsNear) > 1e-4 || fabs(_lightsFar - lightsFar) > 1e-4) { + if (fabs(_near - accumulator->kCamera->GetRuntimeData2().viewFrustum.fNear) > 1e-4 || fabs(_far - accumulator->kCamera->GetRuntimeData2().viewFrustum.fFar) > 1e-4 || fabs(_fov - fov) > 1e-4 || fabs(_lightsNear - lightsNear) > 1e-4 || fabs(_lightsFar - lightsFar) > 1e-4) { PerFrameLightCulling perFrameData{}; - perFrameData.InvProjMatrix = DirectX::XMMatrixInverse(nullptr, state->cameraData.getEye().projMatrixUnjittered); + perFrameData.InvProjMatrix[0] = DirectX::XMMatrixInverse(nullptr, projMatrixUnjittered); + if (eyeCount == 1) + perFrameData.InvProjMatrix[1] = perFrameData.InvProjMatrix[0]; + else + perFrameData.InvProjMatrix[1] = DirectX::XMMatrixInverse(nullptr, state->GetVRRuntimeData().cameraData.getEye(1).projMatrixUnjittered); perFrameData.LightsNear = lightsNear; perFrameData.LightsFar = lightsFar; @@ -819,8 +805,8 @@ void LightLimitFix::UpdateLights() ID3D11UnorderedAccessView* null_uav = nullptr; context->CSSetUnorderedAccessViews(0, 1, &null_uav, nullptr); - _near = accumulator->kCamera->viewFrustum.fNear; - _far = accumulator->kCamera->viewFrustum.fFar; + _near = accumulator->kCamera->GetRuntimeData2().viewFrustum.fNear; + _far = accumulator->kCamera->GetRuntimeData2().viewFrustum.fFar; _fov = fov; _lightsNear = lightsNear; _lightsFar = lightsFar; diff --git a/src/Features/LightLimitFix.h b/src/Features/LightLimitFix.h index 43d241801..6415bed11 100644 --- a/src/Features/LightLimitFix.h +++ b/src/Features/LightLimitFix.h @@ -28,8 +28,8 @@ struct LightLimitFix : Feature { float3 color; float radius; - float3 positionWS; - float3 positionVS; + float3 positionWS[2]; + float3 positionVS[2]; uint shadowMode; uint pad; }; @@ -48,7 +48,7 @@ struct LightLimitFix : Feature struct alignas(16) PerFrameLightCulling { - float4x4 InvProjMatrix; + float4x4 InvProjMatrix[2]; float LightsNear; float LightsFar; float pad[2]; @@ -74,6 +74,7 @@ struct LightLimitFix : Feature std::unique_ptr perPass = nullptr; bool rendered = false; + int eyeCount = !REL::Module::IsVR() ? 1 : 2; ID3D11ComputeShader* clusterBuildingCS = nullptr; ID3D11ComputeShader* clusterCullingCS = nullptr; @@ -106,6 +107,9 @@ struct LightLimitFix : Feature virtual void DrawSettings(); virtual void Draw(const RE::BSShader* shader, const uint32_t descriptor); + float CalculateLightDistance(float3 a_lightPosition, float a_radius); + bool AddCachedParticleLights(eastl::vector& lightsData, LightLimitFix::LightData& light, float dimmerMult = 1.0f, int eyeIndex = 0, RE::BSGeometry* a_geometry = nullptr, double timer = 0.0f); + void SetLightPosition(LightLimitFix::LightData& a_light, RE::NiPoint3& a_initialPosition); void UpdateLights(); void Bind(); @@ -149,7 +153,7 @@ struct LightLimitFix : Feature }; std::shared_mutex cachedParticleLightsMutex; - eastl::vector cachedParticleLights; + eastl::vector cachedParticleLights[2]; uint32_t particleLightsDetectionHits = 0; float CalculateLuminance(CachedParticleLight& light, RE::NiPoint3& point); diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index 4e174f114..ce415e4c4 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -8,10 +8,10 @@ #include "Features/ExtendedMaterials.h" #include "Features/GrassCollision.h" +#include "Features/LightLimitFix.h" #include "Features/ScreenSpaceShadows.h" #include "Features/WaterBlending.h" #include "State.h" -#include "Features/LightLimitFix.h" namespace SIE { @@ -1055,18 +1055,20 @@ namespace SIE const std::wstring path = GetShaderPath(shader.fxpFilename); std::array defines; + auto lastIndex = 0; if (shaderClass == ShaderClass::Vertex) { - defines[0] = { "VSHADER", nullptr }; + defines[lastIndex++] = { "VSHADER", nullptr }; } else if (shaderClass == ShaderClass::Pixel) { - defines[0] = { "PSHADER", nullptr }; + defines[lastIndex++] = { "PSHADER", nullptr }; } - if (!REL::Module::IsVR()) { - defines[1] = { nullptr, nullptr }; - } else { - defines[1] = { "VR", nullptr }; - defines[2] = { nullptr, nullptr }; + if (State::GetSingleton()->IsDeveloperMode()) { + defines[lastIndex++] = { "D3DCOMPILE_SKIP_OPTIMIZATION", nullptr }; + defines[lastIndex++] = { "D3DCOMPILE_DEBUG", nullptr }; } - GetShaderDefines(type, descriptor, &defines[(1 + (size_t)REL::Module::IsVR())]); + if (REL::Module::IsVR()) + defines[lastIndex++] = { "VR", nullptr }; + defines[lastIndex] = { nullptr, nullptr }; // do final entry + GetShaderDefines(type, descriptor, &defines[lastIndex]); logger::debug("{}, {}", descriptor, MergeDefinesString(defines)); diff --git a/src/Util.cpp b/src/Util.cpp index 17b773adb..d3542d03c 100644 --- a/src/Util.cpp +++ b/src/Util.cpp @@ -1,4 +1,5 @@ #include "Util.h" +#include "State.h" #include @@ -120,7 +121,10 @@ namespace Util if (REL::Module::IsVR()) macros.push_back({ "VR", "" }); - + if (State::GetSingleton()->IsDeveloperMode()) { + macros.push_back({ "D3DCOMPILE_SKIP_OPTIMIZATION", "" }); + macros.push_back({ "D3DCOMPILE_DEBUG", "" }); + } if (!_stricmp(ProgramType, "ps_5_0")) macros.push_back({ "PIXELSHADER", "" }); else if (!_stricmp(ProgramType, "vs_5_0"))