diff --git a/features/Grass Lighting/Shaders/Features/GrassLighting.ini b/features/Grass Lighting/Shaders/Features/GrassLighting.ini index 140421b6e6..5e77fe691c 100644 --- a/features/Grass Lighting/Shaders/Features/GrassLighting.ini +++ b/features/Grass Lighting/Shaders/Features/GrassLighting.ini @@ -1,2 +1,2 @@ [Info] -Version = 2-0-3 +Version = 2-1-0 diff --git a/features/Grass Lighting/Shaders/GrassLighting/GrassLighting.hlsli b/features/Grass Lighting/Shaders/GrassLighting/GrassLighting.hlsli index f85808a56d..cbcd8b71de 100644 --- a/features/Grass Lighting/Shaders/GrassLighting/GrassLighting.hlsli +++ b/features/Grass Lighting/Shaders/GrassLighting/GrassLighting.hlsli @@ -1,10 +1,27 @@ +#include "Common/BRDF.hlsli" +#include "Common/Color.hlsli" + namespace GrassLighting { - float3 GetLightSpecularInput(float3 L, float3 V, float3 N, float3 lightColor, float shininess) + float3 GetLightSpecularInput(float3 L, float3 V, float3 N, float3 lightColor, float roughness, float3 F0) { float3 H = normalize(V + L); - float HdotN = saturate(dot(H, N)); +#if defined(VANILLA_FRESNEL) + if (SharedData::vanillaFresnelSettings.Enable && SharedData::vanillaFresnelSettings.EnableGGXOnGrass) { + float NdotL = saturate(dot(N, L)); + float NdotV = saturate(dot(N, V)); + float NdotH = saturate(dot(N, H)); + float VdotH = saturate(dot(V, H)); + float D = BRDF::D_GGX(roughness, NdotH); + float G = BRDF::Vis_SmithJointApprox(roughness, NdotL, NdotV); + float3 F = BRDF::F_Schlick(F0, VdotH); + float3 specular = D * G * F; + return specular * lightColor * NdotL * Color::PBRLightingCompensation; + } +#endif + float shininess = (1.0 - roughness) * 100.f; + float HdotN = saturate(dot(H, N)); float lightColorMultiplier = exp2(shininess * log2(HdotN)); return lightColor * lightColorMultiplier.xxx; } diff --git a/features/Vanilla Fresnel/CORE b/features/Vanilla Fresnel/CORE new file mode 100644 index 0000000000..e69de29bb2 diff --git a/features/Vanilla Fresnel/Shaders/Features/VanillaFresnel.ini b/features/Vanilla Fresnel/Shaders/Features/VanillaFresnel.ini new file mode 100644 index 0000000000..19f01444dc --- /dev/null +++ b/features/Vanilla Fresnel/Shaders/Features/VanillaFresnel.ini @@ -0,0 +1,2 @@ +[Info] +Version = 1-0-0 \ No newline at end of file diff --git a/package/Shaders/Common/LightingEval.hlsli b/package/Shaders/Common/LightingEval.hlsli index 21748a03ec..2cf7a857cf 100644 --- a/package/Shaders/Common/LightingEval.hlsli +++ b/package/Shaders/Common/LightingEval.hlsli @@ -95,6 +95,25 @@ float3 VanillaSpecular(DirectContext context, float shininess, float2 uv, float2 return lightColorMultiplier; } +float3 MicrofacetSpecular(DirectContext context, float3 F0, float roughness) +{ + const float3 N = context.worldNormal; + const float3 V = context.viewDir; + const float3 L = context.lightDir; + const float3 H = context.halfVector; + + float NdotL = clamp(dot(N, L), EPSILON_DOT_CLAMP, 1); + float NdotV = saturate(abs(dot(N, V)) + EPSILON_DOT_CLAMP); + float NdotH = saturate(dot(N, H)); + float VdotH = saturate(dot(V, H)); + + float D = BRDF::D_GGX(roughness, NdotH); + float G = BRDF::Vis_SmithJointApprox(roughness, NdotV, NdotL); + float3 F = BRDF::F_Schlick(F0, VdotH); + + return D * G * F * NdotL; +} + void EvaluateLighting(DirectContext context, MaterialProperties material, float3x3 tbnTr, float2 uv, float2 uv_ddx, float2 uv_ddy, out DirectLightingOutput lightingOutput) { lightingOutput = (DirectLightingOutput)0; @@ -145,6 +164,17 @@ void EvaluateLighting(DirectContext context, MaterialProperties material, float3 # if defined(BACK_LIGHTING) lightingOutput.diffuse += softLightColor * saturate(-NdotL) * material.backLightColor * Color::VanillaNormalization(); # endif + +# if defined(VANILLA_FRESNEL) + if (SharedData::vanillaFresnelSettings.Enable && SharedData::vanillaFresnelSettings.EnableGGX) { + lightingOutput.specular = diffuseLightColor * MicrofacetSpecular(context, material.F0, material.Roughness) * Color::PBRLightingCompensation * Color::PBRLightingScale; + + float2 specularBRDF = BRDF::EnvBRDF(material.Roughness, saturate(dot(context.worldNormal, context.viewDir))); + lightingOutput.specular *= 1 + material.F0 * (1 / (specularBRDF.x + specularBRDF.y) - 1); + return; + } +# endif + lightingOutput.specular = VanillaSpecular(context, material.Shininess, uv, uv_ddx, uv_ddy) * material.SpecularColor * material.Glossiness * diffuseLightColor * Color::VanillaNormalization(); #endif } diff --git a/package/Shaders/Common/Permutation.hlsli b/package/Shaders/Common/Permutation.hlsli index d3e2005526..41054c5a86 100644 --- a/package/Shaders/Common/Permutation.hlsli +++ b/package/Shaders/Common/Permutation.hlsli @@ -75,6 +75,7 @@ namespace Permutation static const uint GrassSphereNormal = (1 << 3); static const uint IsSun = (1 << 4); static const uint SuppressExternalEmittance = (1 << 5); + static const uint IsEye = (1 << 6); } namespace ExtraFeatureFlags diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 4902e7abae..6041607aa7 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -310,6 +310,22 @@ namespace SharedData float4 wetParams; }; + struct VanillaFresnelSettings + { + uint Enable; + uint EnableGGX; + uint EnableGGXOnGrass; + uint EnableDynamicCubemapsConversion; + uint EnableEyeSpecialHandling; + float RoughnessMultiplier; + float SpecularRoughnessBlend; + float BaseF0Multiplier; + float MinF0; + float CubemapToF0Multiplier; + float ComplexMaterialF0Multiplier; + float pad; + }; + cbuffer FeatureData : register(b6) { GrassLightingSettings grassLightingSettings; @@ -330,6 +346,7 @@ namespace SharedData ExponentialHeightFogSettings exponentialHeightFogSettings; TruePBRSettings truePBRSettings; SkinData skinData; + VanillaFresnelSettings vanillaFresnelSettings; }; Texture2D DepthTexture : register(t17); diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 90ec257f1e..9b7d683c83 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -1877,6 +1877,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) shininess = lerp(shininess, shininess * complexMaterialColor.y, complexMaterial); if (complexMaterial) { complexSpecular = lerp(1.0, baseColor.xyz, complexMaterialColor.z); +# if defined(VANILLA_FRESNEL) + complexSpecular = saturate(complexSpecular * (SharedData::vanillaFresnelSettings.Enable ? SharedData::vanillaFresnelSettings.ComplexMaterialF0Multiplier : 1.0)); +# endif baseColor.xyz = lerp(baseColor.xyz, 0.0, complexMaterialColor.z); } # endif // defined (EMAT) && defined(ENVMAP) @@ -2283,7 +2286,27 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(BACK_LIGHTING) material.backLightColor = backLightColor.xyz; # endif -# endif // TRUE_PBR + +# if defined(VANILLA_FRESNEL) + const bool enableVanillaFresnel = SharedData::vanillaFresnelSettings.Enable; + const bool isEyeMaterial = (Permutation::ExtraShaderDescriptor & Permutation::ExtraFlags::IsEye) != 0; + material.F0 = enableVanillaFresnel ? SharedData::vanillaFresnelSettings.MinF0 : 0.0; +# if defined(SPECULAR) + if (enableVanillaFresnel && !isEyeMaterial) { + material.F0 = saturate(glossiness * SpecularColor.xyz / Math::PI); + float roughnessFromSpecular = (1.0 - glossiness) * (1.0 - glossiness); + float roughnessFromShininess = ShininessToRoughness(material.Shininess); + material.Roughness = lerp(roughnessFromShininess, roughnessFromSpecular, SharedData::vanillaFresnelSettings.SpecularRoughnessBlend * (1.0 - glossiness)); + } +# endif + if (enableVanillaFresnel && isEyeMaterial && SharedData::vanillaFresnelSettings.EnableEyeSpecialHandling) { + material.F0 = 0.027; + material.Roughness = 0.1; + } + material.F0 = max((enableVanillaFresnel ? SharedData::vanillaFresnelSettings.MinF0 : 0.0), material.F0 * SharedData::vanillaFresnelSettings.BaseF0Multiplier); + const float3 baseF0 = material.F0; +# endif // VANILLA_FRESNEL +# endif // TRUE_PBR # if defined(SKIN) && defined(CS_SKIN) const float ExtraRoughness = BRDF::F_Schlick(0.04, saturate(dot(worldNormal.xyz, viewDirection))) * SharedData::skinData.fuzzParams.w; @@ -2352,10 +2375,16 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) uint2 envSize; TexEnvSampler.GetDimensions(envSize.x, envSize.y); -# if defined(EMAT) +# if defined(EMAT) && !defined(VANILLA_FRESNEL) if (envSize.x == 1 && envSize.y == 1 || complexMaterial) { +# elif defined(VANILLA_FRESNEL) + bool vfStartDynamicCubemapTest = (enableVanillaFresnel && SharedData::vanillaFresnelSettings.EnableDynamicCubemapsConversion); +# if defined(EMAT) + vfStartDynamicCubemapTest = complexMaterial || vfStartDynamicCubemapTest; +# endif + if (vfStartDynamicCubemapTest || (envSize.x == 1 && envSize.y == 1)) { # else - if (envSize.x == 1 && envSize.y == 1) { + if (envSize.x == 1 && envSize.y == 1) { # endif dynamicCubemap = true; @@ -2375,15 +2404,26 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) } # endif +# if defined(VANILLA_FRESNEL) + dynamicCubemap = dynamicCubemap || (SharedData::vanillaFresnelSettings.EnableDynamicCubemapsConversion && enableVanillaFresnel); + const float originRoughness = material.Roughness; +# endif + if (dynamicCubemap) { float4 envColorBase = TexEnvSampler.SampleLevel(SampEnvSampler, float3(1.0, 0.0, 0.0), 15); + bool usingDynamicCubemap = envColorBase.a < 1.0; - if (envColorBase.a < 1.0) { + if (usingDynamicCubemap) { material.F0 = Color::SkyrimGammaToLinear(envColorBase.rgb); material.Roughness = envColorBase.a; } else { +# if defined(VANILLA_FRESNEL) + material.F0 = saturate(Color::SkyrimGammaToLinear(envColorBase.rgb) * Math::PI * SharedData::vanillaFresnelSettings.CubemapToF0Multiplier); + material.Roughness = material.Roughness; +# else material.F0 = 1.0; material.Roughness = 1.0 / 7.0; +# endif } # if defined(CREATOR) @@ -2398,7 +2438,39 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) material.Roughness = lerp(material.Roughness, complexMaterialRoughness, complexMaterial); material.F0 = lerp(material.F0, complexSpecular, complexMaterial); # endif + + envMask = saturate(envMask); +# if defined(VANILLA_FRESNEL) + if (enableVanillaFresnel) { +# if defined(SPECULAR) + material.F0 = max(lerp(baseF0, material.F0, envMask), SharedData::vanillaFresnelSettings.MinF0); +# else + material.F0 = max(lerp(0, material.F0, envMask), SharedData::vanillaFresnelSettings.MinF0); +# endif +# if defined(EMAT) + if (!complexMaterial) { +# endif + material.Roughness = lerp(originRoughness, material.Roughness, envMask); + if (!isEyeMaterial && !usingDynamicCubemap) { + material.F0 = saturate(material.F0 + envMask * material.BaseColor); + material.BaseColor = lerp(material.BaseColor, 0, envMask); + } +# if defined(EMAT) + } +# endif + } +# endif + } + } +# endif + +# if defined(VANILLA_FRESNEL) && !defined(TRUE_PBR) + if (enableVanillaFresnel) { + if (isEyeMaterial && SharedData::vanillaFresnelSettings.EnableEyeSpecialHandling) { + material.F0 = 0.027; + material.Roughness = 0.1; } + material.Roughness = clamp(material.Roughness * SharedData::vanillaFresnelSettings.RoughnessMultiplier, 0.04, 1.0); } # endif @@ -3072,7 +3144,10 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif # endif # if defined(ENVMAP) || defined(MULTI_LAYER_PARALLAX) || defined(EYE) - indirectLobeWeights.specular *= envMask; +# if defined(VANILLA_FRESNEL) + if (!enableVanillaFresnel) +# endif + indirectLobeWeights.specular *= envMask; # endif # if defined(SPECULAR) && !defined(TRUE_PBR) @@ -3115,12 +3190,18 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # if defined(DYNAMIC_CUBEMAPS) if (!dynamicCubemap) # endif - specularColor += envColor * Color::IrradianceToLinear(diffuseColor); +# if defined(VANILLA_FRESNEL) + if (!(enableVanillaFresnel && SharedData::vanillaFresnelSettings.EnableDynamicCubemapsConversion)) +# endif + specularColor += envColor * Color::IrradianceToLinear(diffuseColor); indirectLobeWeights.diffuse += envColor; # endif # if defined(EMAT_ENVMAP) - specularColor *= complexSpecular; +# if defined(VANILLA_FRESNEL) + if (!(enableVanillaFresnel && SharedData::vanillaFresnelSettings.EnableGGX)) +# endif + specularColor *= complexSpecular; # endif // defined (EMAT) && defined(ENVMAP) # if defined(LOD_LAND_BLEND) && defined(TRUE_PBR) diff --git a/package/Shaders/RunGrass.hlsl b/package/Shaders/RunGrass.hlsl index a24bac33af..f680613f41 100644 --- a/package/Shaders/RunGrass.hlsl +++ b/package/Shaders/RunGrass.hlsl @@ -279,9 +279,10 @@ struct PS_OUTPUT float4 NormalGlossiness: SV_Target2; float4 Albedo: SV_Target3; float4 Specular: SV_Target4; + float4 Reflectance: SV_Target5; float4 Masks: SV_Target6; float4 Masks2: SV_Target7; -# endif // RENDER_DEPTH +# endif // RENDER_DEPTH }; #else struct PS_OUTPUT @@ -303,8 +304,6 @@ struct PS_OUTPUT SamplerState SampBaseSampler : register(s0); SamplerState SampShadowMaskSampler : register(s1); - - Texture2D TexBaseSampler : register(t0); Texture2D TexShadowMaskSampler : register(t1); @@ -315,8 +314,6 @@ cbuffer PerFrame : register(b0) float4 cb0_2[7] : packoffset(c3); } - - cbuffer AlphaTestRefCB : register(b11) { float AlphaTestRefRS : packoffset(c0); @@ -338,16 +335,13 @@ cbuffer AlphaTestRefCB : register(b11) # if defined(SKYLIGHTING) # define SKYLIGHTING_SHADOW_VIS +# include "Skylighting/Skylighting.hlsli" # endif # if defined(DYNAMIC_CUBEMAPS) # include "DynamicCubemaps/DynamicCubemaps.hlsli" # endif -# if defined(SKYLIGHTING) -# include "Skylighting/Skylighting.hlsli" -# endif - # if defined(IBL) # include "IBL/IBL.hlsli" # endif @@ -429,8 +423,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3x3 tbn = 0; - if (complex) - { + if (complex) { float3 normalColor = GrassLighting::TransformNormal(specColor.xyz); // world-space -> tangent-space -> world-space. // This is because we don't have pre-computed tangents. @@ -441,6 +434,14 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) if (!complex || SharedData::grassLightingSettings.OverrideComplexGrassSettings) baseColor.xyz *= SharedData::grassLightingSettings.BasicGrassBrightness; +# if defined(VANILLA_FRESNEL) + const bool enableVanillaFresnel = SharedData::vanillaFresnelSettings.Enable; + float3 F0 = enableVanillaFresnel ? max(SharedData::vanillaFresnelSettings.MinF0, saturate(specColor.w * SharedData::grassLightingSettings.SpecularStrength * SharedData::vanillaFresnelSettings.BaseF0Multiplier / Math::PI)) : 0.0; +# else + float3 F0 = 0.0; +# endif + float roughness = saturate(1.0 - SharedData::grassLightingSettings.Glossiness * 0.01); + float llDirLightMult = (SharedData::linearLightingSettings.enableLinearLighting && !SharedData::linearLightingSettings.isDirLightLinear) ? SharedData::linearLightingSettings.dirLightMult : 1.0f; float3 dirLightColor = Color::DirectionalLight(SharedData::DirLightColor.xyz / max(llDirLightMult, 1e-5), SharedData::linearLightingSettings.isDirLightLinear) * llDirLightMult; float3 dirLightColorMultiplier = 1; @@ -485,27 +486,28 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float vertexAO = max(max(vertexColor.r, vertexColor.g), vertexColor.b); vertexColor /= max(vertexAO, EPSILON_DIVISION); -# if defined(SKYLIGHTING) +# if defined(SKYLIGHTING) float3 positionMSSkylight = input.WorldPosition.xyz; sh2 skylightingSH = Skylighting::Sample(positionMSSkylight, normal -# if defined(SKYLIGHTING_SHADOW_VIS) - , skylightingShadowVisibility -# endif +# if defined(SKYLIGHTING_SHADOW_VIS) + , + skylightingShadowVisibility +# endif ); float skylightingDiffuse = Skylighting::GetSkylightingDiffuse(skylightingSH, positionMSSkylight, normal, vertexAO); -# endif // SKYLIGHTING +# endif // SKYLIGHTING float3 albedo = baseColor.xyz * vertexColor; float dirSoftShadow = dirDetailedShadow; -# if defined(SKYLIGHTING_SHADOW_VIS) +# if defined(SKYLIGHTING_SHADOW_VIS) dirSoftShadow = skylightingShadowVisibility; -# endif +# endif float3 subsurfaceColor = dirLightColor * dirSoftShadow * (GetSoftLightMultiplier(dirLightAngle, softLightRolloff)) * Color::VanillaNormalization(); if (complex) - lightsSpecularColor += dirDetailedShadow * GrassLighting::GetLightSpecularInput(SharedData::DirLightDirection.xyz, viewDirection, normal, dirLightColor, SharedData::grassLightingSettings.Glossiness) * Color::VanillaNormalization(); + lightsSpecularColor += dirDetailedShadow * GrassLighting::GetLightSpecularInput(SharedData::DirLightDirection.xyz, viewDirection, normal, dirLightColor, roughness, F0) * Color::VanillaNormalization(); # if defined(LIGHT_LIMIT_FIX) uint clusterIndex = 0; @@ -560,7 +562,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) lightsDiffuseColor += lightDiffuseColor * Color::VanillaNormalization(); if (complex) - lightsSpecularColor += GrassLighting::GetLightSpecularInput(normalizedLightDirection, viewDirection, normal, lightColor, SharedData::grassLightingSettings.Glossiness) * Color::VanillaNormalization(); + lightsSpecularColor += GrassLighting::GetLightSpecularInput(normalizedLightDirection, viewDirection, normal, lightColor, roughness, F0) * Color::VanillaNormalization(); } } } @@ -570,15 +572,15 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) float3 directionalAmbientColor = Color::Ambient(max(0, SharedData::GetAmbient(normal))); -# if defined(IBL) +# if defined(IBL) if (SharedData::iblSettings.EnableIBL) { -# if defined(SKYLIGHTING) +# if defined(SKYLIGHTING) directionalAmbientColor = ImageBasedLighting::GetDiffuseIBLOccluded(directionalAmbientColor, -normal, skylightingDiffuse); -# else +# else directionalAmbientColor = ImageBasedLighting::GetDiffuseIBL(directionalAmbientColor, -normal); -# endif - } # endif + } +# endif diffuseColor += directionalAmbientColor; diffuseColor += subsurfaceColor * albedo; @@ -586,17 +588,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) directionalAmbientColor *= albedo; -# if defined(SKYLIGHTING) -# if defined(IBL) +# if defined(SKYLIGHTING) +# if defined(IBL) if (!SharedData::iblSettings.EnableIBL) -# endif +# endif { Skylighting::ApplySkylighting(diffuseColor, directionalAmbientColor, albedo, skylightingDiffuse); } -# endif +# endif specularColor += lightsSpecularColor; - specularColor *= specColor.w * SharedData::grassLightingSettings.SpecularStrength; +# if defined(VANILLA_FRESNEL) + if (!(SharedData::vanillaFresnelSettings.Enable && SharedData::vanillaFresnelSettings.EnableGGXOnGrass)) +# endif + specularColor *= specColor.w * SharedData::grassLightingSettings.SpecularStrength; # if defined(LIGHT_LIMIT_FIX) && defined(LLFDEBUG) if (SharedData::lightLimitFixSettings.EnableLightsVisualisation) { @@ -615,8 +620,18 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) # endif float3 normalVS = normalize(FrameBuffer::WorldToView(normal, false)); + + float3 reflectance = 0; +# if defined(DYNAMIC_CUBEMAPS) && defined(VANILLA_FRESNEL) + if (SharedData::vanillaFresnelSettings.Enable) { + float2 specularBDRF = BRDF::EnvBRDF(roughness, saturate(dot(viewDirection, normal))); + reflectance = F0 * specularBDRF.x + specularBDRF.y; + } +# endif + + psout.Reflectance = float4(reflectance, 1); psout.Albedo = float4(albedo, 1); - psout.NormalGlossiness = float4(GBuffer::EncodeNormal(normalVS), specColor.w, 1); + psout.NormalGlossiness = float4(GBuffer::EncodeNormal(normalVS), 1.0 - roughness, 1); psout.Specular = float4(specularColor, 1); psout.Masks = float4(0, 0, Color::RGBToYCoCg(directionalAmbientColor).x, 0); @@ -735,7 +750,8 @@ PS_OUTPUT main(PS_INPUT input) float3 positionMSSkylight = input.WorldPosition.xyz; sh2 skylightingSH = Skylighting::Sample(positionMSSkylight, normal # if defined(SKYLIGHTING_SHADOW_VIS) - , skylightingShadowVisibility + , + skylightingShadowVisibility # endif ); float skylightingDiffuse = Skylighting::GetSkylightingDiffuse(skylightingSH, positionMSSkylight, normal, vertexAO); diff --git a/src/Feature.cpp b/src/Feature.cpp index d92c15cf93..bd65a21e7b 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -35,6 +35,7 @@ #include "Features/TerrainVariation.h" #include "Features/UnifiedWater.h" #include "Features/Upscaling.h" +#include "Features/VanillaFresnel.h" #include "Features/VolumetricLighting.h" #include "Features/VolumetricShadows.h" #include "Features/WaterEffects.h" @@ -237,6 +238,7 @@ const std::vector& Feature::GetFeatureList() &globals::features::skySync, &globals::features::terrainBlending, &globals::features::terrainHelper, + &globals::features::vanillaFresnel, &globals::features::volumetricLighting, &globals::features::lodBlending, &globals::features::inverseSquareLighting, diff --git a/src/FeatureBuffer.cpp b/src/FeatureBuffer.cpp index eaef6d6cdc..5de7bda2dc 100644 --- a/src/FeatureBuffer.cpp +++ b/src/FeatureBuffer.cpp @@ -18,6 +18,7 @@ #include "Features/TerrainBlending.h" #include "Features/TerrainShadows.h" #include "Features/TerrainVariation.h" +#include "Features/VanillaFresnel.h" #include "Features/WetnessEffects.h" #include "TruePBR.h" @@ -60,5 +61,6 @@ std::pair GetFeatureBufferData(bool a_inWorld) globals::features::terrainBlending.settings, globals::features::exponentialHeightFog.settings, globals::features::truePBR.settings, - globals::features::skin.GetCommonBufferData()); -} \ No newline at end of file + globals::features::skin.GetCommonBufferData(), + globals::features::vanillaFresnel.settings); +} diff --git a/src/Features/VanillaFresnel.cpp b/src/Features/VanillaFresnel.cpp new file mode 100644 index 0000000000..0a1cb3dda7 --- /dev/null +++ b/src/Features/VanillaFresnel.cpp @@ -0,0 +1,127 @@ +#include "VanillaFresnel.h" + +#include "Globals.h" +#include "ShaderCache.h" +#include "State.h" + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( + VanillaFresnel::Settings, + Enable, + EnableGGX, + EnableGGXOnGrass, + EnableDynamicCubemapsConversion, + EnableEyeSpecialHandling, + RoughnessMultiplier, + SpecularRoughnessBlend, + BaseF0Multiplier, + MinF0, + CubemapToF0Multiplier, + ComplexMaterialF0Multiplier) + +namespace +{ + constexpr auto IsEyeDescriptor = static_cast(State::ExtraShaderDescriptors::IsEye); + + bool ContainsEye(std::string_view a_name) + { + constexpr std::string_view eye = "eye"; + return std::search( + a_name.begin(), a_name.end(), + eye.begin(), eye.end(), + [](char a_lhs, char a_rhs) { + return static_cast(std::tolower(static_cast(a_lhs))) == a_rhs; + }) != a_name.end(); + } + + bool IsEyePass(const RE::BSLightingShader* a_shader, const RE::BSRenderPass* a_pass) + { + if (a_shader) { + const auto technique = static_cast(0x3F & (a_shader->currentRawTechnique >> 24)); + if (technique == SIE::ShaderCache::LightingShaderTechniques::Eye) { + return true; + } + } + + if (!a_pass || !a_pass->shaderProperty) { + return false; + } + + const auto* lightingProperty = a_pass->shaderProperty->GetRTTI() == globals::rtti::BSLightingShaderPropertyRTTI.get() ? + static_cast(a_pass->shaderProperty) : + nullptr; + if (!lightingProperty || !lightingProperty->material) { + return false; + } + + const auto feature = lightingProperty->material->GetFeature(); + if (feature == RE::BSShaderMaterial::Feature::kEye) { + return true; + } + + return feature == RE::BSShaderMaterial::Feature::kEnvironmentMap && + a_pass->geometry && + ContainsEye(a_pass->geometry->name.c_str()); + } + + void UpdateEyePermutation(const RE::BSLightingShader* a_shader, const RE::BSRenderPass* a_pass) + { + auto& descriptor = globals::state->permutationData.ExtraShaderDescriptor; + descriptor &= ~IsEyeDescriptor; + + if (IsEyePass(a_shader, a_pass)) { + descriptor |= IsEyeDescriptor; + } + } + + struct BSLightingShader_SetupGeometry + { + static void thunk(RE::BSLightingShader* a_shader, RE::BSRenderPass* a_pass, uint32_t a_renderFlags) + { + UpdateEyePermutation(a_shader, a_pass); + func(a_shader, a_pass, a_renderFlags); + } + static inline REL::Relocation func; + }; +} + +void VanillaFresnel::PostPostLoad() +{ + stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); + logger::info("[VanillaFresnel] Installed hooks - BSLightingShader_SetupGeometry"); +} + +void VanillaFresnel::RestoreDefaultSettings() +{ + settings = {}; +} + +void VanillaFresnel::LoadSettings(json& o_json) +{ + settings = o_json; +} + +void VanillaFresnel::SaveSettings(json& o_json) +{ + o_json = settings; +} + +void VanillaFresnel::DrawSettings() +{ + ImGui::Checkbox("Enable Vanilla Fresnel", reinterpret_cast(&settings.Enable)); + ImGui::Checkbox("Enable Phong to GGX", reinterpret_cast(&settings.EnableGGX)); + ImGui::Checkbox("Enable Phong to GGX on Grass", reinterpret_cast(&settings.EnableGGXOnGrass)); + if (!settings.EnableGGX) { + settings.EnableDynamicCubemapsConversion = false; + } + ImGui::BeginDisabled(!settings.EnableGGX); + ImGui::Checkbox("Enable Auto Cubemaps Conversion", reinterpret_cast(&settings.EnableDynamicCubemapsConversion)); + ImGui::EndDisabled(); + ImGui::Checkbox("Enable Eye Special Handling", reinterpret_cast(&settings.EnableEyeSpecialHandling)); + + ImGui::SliderFloat("Roughness Multiplier", &settings.RoughnessMultiplier, 0.0f, 10.0f, "%.2f"); + ImGui::SliderFloat("Specular Roughness Blend", &settings.SpecularRoughnessBlend, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat("Base F0 Multiplier", &settings.BaseF0Multiplier, 0.0f, 10.0f, "%.2f"); + ImGui::SliderFloat("Min F0", &settings.MinF0, 0.0f, 0.04f, "%.3f"); + ImGui::SliderFloat("Cubemap to F0 Multiplier", &settings.CubemapToF0Multiplier, 0.0f, 10.0f, "%.2f"); + ImGui::SliderFloat("Complex Material Env F0 Multiplier", &settings.ComplexMaterialF0Multiplier, 0.0f, 10.0f, "%.2f"); +} diff --git a/src/Features/VanillaFresnel.h b/src/Features/VanillaFresnel.h new file mode 100644 index 0000000000..733cd9dcfc --- /dev/null +++ b/src/Features/VanillaFresnel.h @@ -0,0 +1,48 @@ +#pragma once + +struct VanillaFresnel : public Feature +{ + ////////////////////////////////////////////////// Boilerplate + // Metadata + virtual inline std::string GetName() override { return "Vanilla Fresnel"; } + virtual inline std::string GetShortName() override { return "VanillaFresnel"; } + virtual inline std::string_view GetCategory() const override { return "Lighting"; } + virtual inline std::pair> GetFeatureSummary() override + { + return { + "Add realistic environmental reflections to vanilla materials.", + { "Add environmental reflections to all materials", + "Supports vanilla and complex materials", + "Optionally turn vanilla phong specular into GGX", + "Optionally turn static cubemaps into dynamic reflections" } + }; + } + + // Functionality + virtual bool inline IsCore() const override { return true; } + virtual inline std::string_view GetShaderDefineName() override { return "VANILLA_FRESNEL"; } + virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + virtual void PostPostLoad() override; + + // Settings & UI + virtual void RestoreDefaultSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + virtual void DrawSettings() override; + + struct alignas(16) Settings + { + uint Enable = true; + uint EnableGGX = false; + uint EnableGGXOnGrass = false; + uint EnableDynamicCubemapsConversion = false; + uint EnableEyeSpecialHandling = true; + float RoughnessMultiplier = 1.0f; + float SpecularRoughnessBlend = 1.0f; + float BaseF0Multiplier = 0.32f; + float MinF0 = 0.02f; + float CubemapToF0Multiplier = 1.0f; + float ComplexMaterialF0Multiplier = 1.0f; + float pad; + } settings; +}; diff --git a/src/Globals.cpp b/src/Globals.cpp index e5cd7e0723..33252e7855 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -1,6 +1,7 @@ #include "Globals.h" #include "Deferred.h" +#include "Features/CSEditor.h" #include "Features/CloudShadows.h" #include "Features/DynamicCubemaps.h" #include "Features/ExponentialHeightFog.h" @@ -33,10 +34,10 @@ #include "Features/TerrainVariation.h" #include "Features/UnifiedWater.h" #include "Features/Upscaling.h" +#include "Features/VanillaFresnel.h" #include "Features/VolumetricLighting.h" #include "Features/VolumetricShadows.h" #include "Features/WaterEffects.h" -#include "Features/CSEditor.h" #include "Features/WetnessEffects.h" #include "Menu.h" #include "SceneSettingsManager.h" @@ -81,6 +82,7 @@ namespace globals TerrainHelper terrainHelper{}; TerrainShadows terrainShadows{}; UnifiedWater unifiedWater{}; + VanillaFresnel vanillaFresnel{}; VolumetricLighting volumetricLighting{}; WaterEffects waterEffects{}; PerformanceOverlay performanceOverlay{}; diff --git a/src/Globals.h b/src/Globals.h index 716ef580e2..66b622b7d2 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -26,6 +26,7 @@ struct TerrainBlending; struct TerrainHelper; struct TerrainShadows; struct UnifiedWater; +struct VanillaFresnel; struct VolumetricLighting; struct WaterEffects; struct PerformanceOverlay; @@ -114,6 +115,7 @@ namespace globals extern TerrainHelper terrainHelper; extern TerrainShadows terrainShadows; extern UnifiedWater unifiedWater; + extern VanillaFresnel vanillaFresnel; extern VolumetricLighting volumetricLighting; extern WaterEffects waterEffects; extern PerformanceOverlay performanceOverlay; diff --git a/src/State.h b/src/State.h index 2e6540ad99..b68537394e 100644 --- a/src/State.h +++ b/src/State.h @@ -268,7 +268,8 @@ class State IsBeastRace = 1 << 2, GrassSphereNormal = 1 << 3, IsSun = 1 << 4, - SuppressExternalEmittance = 1 << 5 + SuppressExternalEmittance = 1 << 5, + IsEye = 1 << 6 }; enum class ExtraFeatureDescriptors : uint32_t