diff --git a/features/Grass Lighting/Shaders/Features/GrassLighting.ini b/features/Grass Lighting/Shaders/Features/GrassLighting.ini index 210bbde16e..df22e00504 100644 --- a/features/Grass Lighting/Shaders/Features/GrassLighting.ini +++ b/features/Grass Lighting/Shaders/Features/GrassLighting.ini @@ -1,5 +1,5 @@ [Info] -Version = 2-0-1 +Version = 2-0-2 [Nexus] nexusmodid = 86502 diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index e64cbd3f5b..c22ed1832e 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -39,9 +39,8 @@ namespace SharedData bool OverrideComplexGrassSettings; float BasicGrassBrightness; - bool EnableWrappedLighting; float ComplexGrassThreshold; - float1 pad0; + float2 pad0; }; struct CPMSettings diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 1fed3c69c6..8a4c3c3578 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -2871,6 +2871,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } # endif +# if defined(LANDSCAPE) + if (SharedData::lodBlendingSettings.DisableTerrainVertexColors) + input.Color.xyz = 1; + else + input.Color.xyz /= max(max(max(input.Color.x, input.Color.y), input.Color.z), EPSILON_DIVISION); +# endif + # if defined(HAIR) float3 vertexColor = lerp(1, Color::ColorToLinear(TintColor.xyz), Color::ColorToLinear(input.Color.y)); # if defined(CS_HAIR) diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index c0e82d5ca8..975ca38c3b 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -36,7 +36,7 @@ struct VS_INPUT struct VS_OUTPUT { float4 HPosition: SV_POSITION0; - float4 VertexColor: COLOR0; + float4 Color: COLOR0; float VertexMult: COLOR1; float3 TexCoord: TEXCOORD0; float3 ViewSpacePosition: @@ -65,7 +65,7 @@ struct VS_OUTPUT struct VS_OUTPUT { float4 HPosition: SV_POSITION0; - float4 VertexColor: COLOR0; + float4 Color: COLOR0; float VertexMult: COLOR1; float3 TexCoord: TEXCOORD0; float4 AmbientColor: TEXCOORD1; @@ -228,8 +228,8 @@ VS_OUTPUT main(VS_INPUT input) # endif // Note: input.Color.w is used for wind speed - vsout.VertexColor.xyz = input.Color.xyz; - vsout.VertexColor.w = distanceFade * perInstanceFade; + vsout.Color.xyz = input.Color.xyz; + vsout.Color.w = distanceFade * perInstanceFade; vsout.VertexMult = input.InstanceData1.w; vsout.TexCoord.xy = input.TexCoord.xy; @@ -305,8 +305,8 @@ VS_OUTPUT main(VS_INPUT input) float distanceFade = 1 - saturate((length(projSpacePosition.xyz) - AlphaParam1) / AlphaParam2); # endif - vsout.VertexColor.xyz = input.Color.xyz; - vsout.VertexColor.w = distanceFade * perInstanceFade; + vsout.Color.xyz = input.Color.xyz; + vsout.Color.w = distanceFade * perInstanceFade; vsout.VertexMult = input.InstanceData1.w; vsout.TexCoord.xy = input.TexCoord.xy; @@ -466,6 +466,15 @@ cbuffer PerMaterial : register(b1) # include "GrassLighting/GrassLighting.hlsli" +float GetSoftLightMultiplier(float angle, float rolloff) +{ + float softLight = saturate((rolloff + angle) / (1 + rolloff)); + float arg1 = (softLight * softLight) * (3 - 2 * softLight); + float clampedAngle = saturate(angle); + float arg2 = (clampedAngle * clampedAngle) * (3 - 2 * clampedAngle); + return saturate(arg1 - arg2); +} + PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) { PS_OUTPUT psout = (PS_OUTPUT)0; @@ -493,7 +502,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) baseColor.xyz = Color::Diffuse(baseColor.xyz); # if defined(RENDER_DEPTH) - float diffuseAlpha = input.VertexColor.w * baseColor.w; + float diffuseAlpha = input.Color.w * baseColor.w; if ((diffuseAlpha - AlphaTestRefRS) < 0) { discard; } @@ -504,6 +513,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) psout.PS.xyz = input.Depth.xxx / input.Depth.yyy; psout.PS.w = diffuseAlpha; # else + if (SharedData::lodBlendingSettings.DisableTerrainVertexColors) + input.Color.xyz = 1; + # if !defined(TRUE_PBR) float4 specColor = complex ? TexBaseSampler.SampleBias(SampBaseSampler, float2(input.TexCoord.x, 0.5 + input.TexCoord.y * 0.5), SharedData::MipBias) : 1; # else @@ -616,18 +628,12 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # else dirLightColor *= dirLightColorMultiplier; - float wrapAmount = saturate(input.VertexNormal.w * 10.0) * 0.5 * (!complex); + float softLightRolloff = saturate(input.VertexNormal.w * 10.0) * SharedData::grassLightingSettings.SubsurfaceScatteringAmount * 2.0; - if (SharedData::grassLightingSettings.EnableWrappedLighting) { - // Old Wrapped Model - float wrappedDirLight = saturate(dirLightAngle + wrapAmount) / (1.0 + wrapAmount); - lightsDiffuseColor += dirLightColor * dirDetailedShadow * saturate(wrappedDirLight) * Color::VanillaNormalization(); - } else { - // Original Standard Model - lightsDiffuseColor += dirLightColor * dirDetailedShadow * saturate(dirLightAngle) * Color::VanillaNormalization(); - } + lightsDiffuseColor += dirLightColor * dirDetailedShadow * saturate(dirLightAngle) * Color::VanillaNormalization(); - float3 vertexColor = Color::ColorToLinear(input.VertexColor.xyz); + float3 vertexColor = Color::ColorToLinear(input.Color.xyz); + vertexColor /= max(max(max(vertexColor.r, vertexColor.g), vertexColor.b), EPSILON_DIVISION); # if defined(SKYLIGHTING) # if defined(VR) @@ -641,7 +647,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 albedo = baseColor.xyz * vertexColor; - float3 subsurfaceColor = dirLightColor * dirDetailedShadow * saturate(-dirLightAngle) * Color::VanillaNormalization(); + float3 subsurfaceColor = dirLightColor * dirDetailedShadow * (GetSoftLightMultiplier(dirLightAngle, softLightRolloff)) * Color::VanillaNormalization(); if (complex) lightsSpecularColor += dirDetailedShadow * GrassLighting::GetLightSpecularInput(SharedData::DirLightDirection.xyz, viewDirection, normal, dirLightColor, SharedData::grassLightingSettings.Glossiness) * Color::VanillaNormalization(); @@ -703,14 +709,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float lightNoL = dot(normalizedLightDirection.xyz, viewDirection); float3 lightDiffuseColor; - if (SharedData::grassLightingSettings.EnableWrappedLighting) { - float wrappedLight = saturate(lightAngle + wrapAmount) / (1.0 + wrapAmount); - lightDiffuseColor = lightColor * wrappedLight; - } else { - lightDiffuseColor = lightColor * saturate(lightAngle); - } + lightDiffuseColor = lightColor * saturate(lightAngle); - subsurfaceColor += lightColor * saturate(-lightAngle) * Color::VanillaNormalization(); + subsurfaceColor += lightColor * GetSoftLightMultiplier(lightAngle, softLightRolloff) * Color::VanillaNormalization(); lightsDiffuseColor += lightDiffuseColor * Color::VanillaNormalization(); @@ -747,7 +748,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif diffuseColor += directionalAmbientColor; - diffuseColor += subsurfaceColor * albedo * SharedData::grassLightingSettings.SubsurfaceScatteringAmount; + diffuseColor += subsurfaceColor * albedo; diffuseColor *= albedo; directionalAmbientColor *= albedo; @@ -805,7 +806,7 @@ PS_OUTPUT main(PS_INPUT input) float4 baseColor = TexBaseSampler.SampleBias(SampBaseSampler, input.TexCoord.xy, SharedData::MipBias); # if defined(RENDER_DEPTH) - float diffuseAlpha = input.VertexColor.w * baseColor.w; + float diffuseAlpha = input.Color.w * baseColor.w; if ((diffuseAlpha - AlphaTestRefRS) < 0) { discard; } @@ -816,6 +817,8 @@ PS_OUTPUT main(PS_INPUT input) psout.PS.xyz = input.Depth.xxx / input.Depth.yyy; psout.PS.w = diffuseAlpha; # else + if (SharedData::lodBlendingSettings.DisableTerrainVertexColors) + input.Color.xyz = 1; uint eyeIndex = Stereo::GetEyeIndexPS(input.HPosition, VPOSOffset); @@ -896,7 +899,8 @@ PS_OUTPUT main(PS_INPUT input) float3 ddy = ddy_coarse(input.WorldPosition); float3 normal = -normalize(cross(ddx, ddy)); - float3 vertexColor = Color::ColorToLinear(input.VertexColor.xyz); + float3 vertexColor = Color::ColorToLinear(input.Color.xyz); + vertexColor /= max(max(max(vertexColor.r, vertexColor.g), vertexColor.b), EPSILON_DIVISION); # if defined(SKYLIGHTING) # if defined(VR) diff --git a/src/Features/GrassCollision.cpp b/src/Features/GrassCollision.cpp index 5ade1f0b5b..3dacd62da4 100644 --- a/src/Features/GrassCollision.cpp +++ b/src/Features/GrassCollision.cpp @@ -30,8 +30,11 @@ void GrassCollision::DrawSettings() } } -void GrassCollision::UpdateCollisions(PerFrame& perFrameData) +void GrassCollision::QueueCollisions() { + if (!settings.EnableGrassCollision) + return; + eastl::vector actorCandidates{}; RE::NiPoint3 cameraPosition = Util::GetEyePosition(0); @@ -83,7 +86,6 @@ void GrassCollision::UpdateCollisions(PerFrame& perFrameData) RE::NiPoint3 centerPos; float radius; if (Util::GetShapeBound(a_object, centerPos, radius)) { - // Cull extremely small collisions if (radius < distance * MIN_COLLISION_RADIUS_DISTANCE_SCALE) return RE::BSVisit::BSVisitControl::kContinue; @@ -140,25 +142,8 @@ void GrassCollision::UpdateCollisions(PerFrame& perFrameData) } } - perFrameData.BoundingBoxCount = std::min((uint)boundingBoxData.size(), MAX_BOUNDING_BOXES); - - auto context = globals::d3d::context; - - if (collisionIndexExtent > 0) { - D3D11_MAPPED_SUBRESOURCE mapped; - DX::ThrowIfFailed(context->Map(collisionInstances->resource.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)); - size_t bytes = sizeof(float4) * collisionIndexExtent; - memcpy_s(mapped.pData, bytes, collisionsData.data(), bytes); - context->Unmap(collisionInstances->resource.get(), 0); - } - - if (perFrameData.BoundingBoxCount > 0) { - D3D11_MAPPED_SUBRESOURCE mapped; - DX::ThrowIfFailed(context->Map(collisionBoundingBoxes->resource.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)); - size_t bytes = sizeof(BoundingBoxPacked) * perFrameData.BoundingBoxCount; - memcpy_s(mapped.pData, bytes, boundingBoxData.data(), bytes); - context->Unmap(collisionBoundingBoxes->resource.get(), 0); - } + queuedBoundingBoxes = std::move(boundingBoxData); + queuedCollisions = std::move(collisionsData); } void GrassCollision::Update() @@ -201,8 +186,28 @@ void GrassCollision::Update() perFrameData.CameraHeightDelta = prevEyePosNI.z - eyePosNI.z; - if (settings.EnableGrassCollision) - UpdateCollisions(perFrameData); + perFrameData.BoundingBoxCount = std::min((uint)queuedBoundingBoxes.size(), MAX_BOUNDING_BOXES); + + auto context = globals::d3d::context; + + if (!queuedCollisions.empty()) { + D3D11_MAPPED_SUBRESOURCE mapped; + DX::ThrowIfFailed(context->Map(collisionInstances->resource.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)); + size_t bytes = sizeof(float4) * queuedCollisions.size(); + memcpy_s(mapped.pData, bytes, queuedCollisions.data(), bytes); + context->Unmap(collisionInstances->resource.get(), 0); + } + + if (perFrameData.BoundingBoxCount > 0) { + D3D11_MAPPED_SUBRESOURCE mapped; + DX::ThrowIfFailed(context->Map(collisionBoundingBoxes->resource.get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped)); + size_t bytes = sizeof(BoundingBoxPacked) * perFrameData.BoundingBoxCount; + memcpy_s(mapped.pData, bytes, queuedBoundingBoxes.data(), bytes); + context->Unmap(collisionBoundingBoxes->resource.get(), 0); + } + + queuedBoundingBoxes.clear(); + queuedCollisions.clear(); perFrame->Update(perFrameData); @@ -211,8 +216,6 @@ void GrassCollision::Update() prevCellID = cellID; prevEyePosNI = eyePosNI; - auto context = globals::d3d::context; - ID3D11Buffer* buffers[1]; buffers[0] = perFrame->CB(); context->VSSetConstantBuffers(5, ARRAYSIZE(buffers), buffers); @@ -252,7 +255,7 @@ void GrassCollision::SetupResources() .Height = 512, .MipLevels = 1, .ArraySize = 1, - .Format = DXGI_FORMAT_R16G16B16A16_UNORM, + .Format = DXGI_FORMAT_R16G16B16A16_FLOAT, .SampleDesc = { .Count = 1 }, .Usage = D3D11_USAGE_DEFAULT, .BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS @@ -324,6 +327,12 @@ bool GrassCollision::HasShaderDefine(RE::BSShader::Type shaderType) } } +void GrassCollision::Hooks::MainUpdate_QueueCollisions::thunk() +{ + func(); + globals::features::grassCollision.QueueCollisions(); +} + void GrassCollision::Hooks::BSGrassShader_SetupGeometry::thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags) { globals::features::grassCollision.Update(); diff --git a/src/Features/GrassCollision.h b/src/Features/GrassCollision.h index 1cbb512e85..6d368027c6 100644 --- a/src/Features/GrassCollision.h +++ b/src/Features/GrassCollision.h @@ -68,6 +68,9 @@ struct GrassCollision : Feature eastl::unique_ptr collisionBoundingBoxes = nullptr; eastl::unique_ptr collisionInstances = nullptr; + eastl::vector queuedBoundingBoxes; + eastl::vector queuedCollisions; + virtual void ClearShaderCache() override; ID3D11ComputeShader* GetCollisionUpdateCS(); @@ -78,7 +81,7 @@ struct GrassCollision : Feature virtual void SetupResources() override; virtual void DrawSettings() override; - void UpdateCollisions(PerFrame& perFrame); + void QueueCollisions(); void Update(); virtual void LoadSettings(json& o_json) override; @@ -98,9 +101,16 @@ struct GrassCollision : Feature static inline REL::Relocation func; }; + struct MainUpdate_QueueCollisions + { + static void thunk(); + static inline REL::Relocation func; + }; + static void Install() { stl::write_vfunc<0x6, BSGrassShader_SetupGeometry>(RE::VTABLE_BSGrassShader[0]); + stl::write_thunk_call(REL::RelocationID(35565, 36564).address() + REL::Relocate(0x748, 0xC26, 0x7EE)); logger::info("[GRASS COLLISION] Installed hooks"); } }; diff --git a/src/Features/GrassLighting.cpp b/src/Features/GrassLighting.cpp index 3b32a6d256..59054832c9 100644 --- a/src/Features/GrassLighting.cpp +++ b/src/Features/GrassLighting.cpp @@ -7,7 +7,6 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( SubsurfaceScatteringAmount, OverrideComplexGrassSettings, BasicGrassBrightness, - EnableWrappedLighting, ComplexGrassThreshold) void GrassLighting::DrawSettings() @@ -53,12 +52,6 @@ void GrassLighting::DrawSettings() } if (ImGui::TreeNodeEx("Lighting", ImGuiTreeNodeFlags_DefaultOpen)) { - ImGui::Checkbox("Enable Wrapped Lighting", (bool*)&settings.EnableWrappedLighting); - if (auto _tt = Util::HoverTooltipWrapper()) { - ImGui::Text("Enables a softer-looking wrapped lighting model from CS 1.3. Useful for certain non-complex grass textures that look too dark during mid day, when the sun is directly overhead."); - } - ImGui::Spacing(); - ImGui::Spacing(); ImGui::Checkbox("Override Complex Grass Lighting Settings", (bool*)&settings.OverrideComplexGrassSettings); if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text( diff --git a/src/Features/GrassLighting.h b/src/Features/GrassLighting.h index e5fe72df2c..67419b7a82 100644 --- a/src/Features/GrassLighting.h +++ b/src/Features/GrassLighting.h @@ -32,12 +32,11 @@ struct GrassLighting : Feature { float Glossiness = 20.0f; float SpecularStrength = 0.5f; - float SubsurfaceScatteringAmount = 0.5f; + float SubsurfaceScatteringAmount = 1.0f; uint OverrideComplexGrassSettings = false; - float BasicGrassBrightness = 1.0f; // Match brightness of ENB - uint EnableWrappedLighting = false; + float BasicGrassBrightness = 1.0f; float ComplexGrassThreshold = 0.03f; - uint pad1; + float2 pad0; }; STATIC_ASSERT_ALIGNAS_16(Settings);