diff --git a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli index 165db9fc5..7db9c0671 100644 --- a/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli +++ b/features/Dynamic Cubemaps/Shaders/DynamicCubemaps/DynamicCubemaps.hlsli @@ -13,6 +13,23 @@ half2 EnvBRDFApprox(half Roughness, half NoV) #if !defined(WATER) +float3 GetDynamicCubemapSpecularIrradiance(float2 uv, float3 N, float3 VN, float3 V, float roughness, float distance) +{ + float3 R = reflect(-V, N); + float level = roughness * 9.0; + + // Horizon specular occlusion + // https://marmosetco.tumblr.com/post/81245981087 + float horizon = min(1.0 + dot(R, VN), 1.0); + horizon *= horizon * horizon; + + float3 specularIrradiance = specularTexture.SampleLevel(SampColorSampler, R, level).xyz; + specularIrradiance *= horizon; + specularIrradiance = sRGB2Lin(specularIrradiance); + + return specularIrradiance; +} + float3 GetDynamicCubemap(float2 uv, float3 N, float3 VN, float3 V, float roughness, float3 F0, float3 diffuseColor, float distance) { float3 R = reflect(-V, N); diff --git a/features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli b/features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli index 5a8b7165e..165fbfd36 100644 --- a/features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli +++ b/features/Extended Materials/Shaders/ExtendedMaterials/ExtendedMaterials.hlsli @@ -5,6 +5,23 @@ // http://www.diva-portal.org/smash/get/diva2:831762/FULLTEXT01.pdf // https://bartwronski.files.wordpress.com/2014/03/ac4_gdc.pdf +struct DisplacementParams +{ + float DisplacementScale; + float DisplacementOffset; + float HeightScale; +}; + +float AdjustDisplacement(float displacement, DisplacementParams params) +{ + return (displacement - 0.5) * params.DisplacementScale + 0.5 + params.DisplacementOffset; +} + +float4 AdjustDisplacement(float4 displacement, DisplacementParams params) +{ + return float4(AdjustDisplacement(displacement.x, params), AdjustDisplacement(displacement.y, params), AdjustDisplacement(displacement.z, params), AdjustDisplacement(displacement.w, params)); +} + float GetMipLevel(float2 coords, Texture2D tex) { // Compute the current gradients: @@ -36,7 +53,7 @@ float GetMipLevel(float2 coords, Texture2D tex) # define HEIGHT_POWER 4.0 # define INV_HEIGHT_POWER 0.25 -float GetTerrainHeight(PS_INPUT input, float2 coords, float mipLevels[6], float blendFactor, out float pixelOffset[6]) +float GetTerrainHeight(PS_INPUT input, float2 coords, float mipLevels[6], DisplacementParams params[6], float blendFactor, out float pixelOffset[6]) { float4 w1 = pow(input.LandBlendWeights1, 1 + 1 * blendFactor); float2 w2 = pow(input.LandBlendWeights2.xy, 1 + 1 * blendFactor); @@ -48,6 +65,20 @@ float GetTerrainHeight(PS_INPUT input, float2 coords, float mipLevels[6], float pixelOffset[3] = 0; pixelOffset[4] = 0; pixelOffset[5] = 0; +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_LandTile0HasDisplacement) != 0) if (w1.x > 0.0) + pixelOffset[0] = w1.x * (0.001 + pow(AdjustDisplacement(TexLandDisplacement0Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[0]).x, params[0]), blendPower)); + [branch] if ((PBRFlags & TruePBR_LandTile1HasDisplacement) != 0) if (w1.y > 0.0) + pixelOffset[1] = w1.y * (0.001 + pow(AdjustDisplacement(TexLandDisplacement1Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[1]).x, params[1]), blendPower)); + [branch] if ((PBRFlags & TruePBR_LandTile2HasDisplacement) != 0) if (w1.z > 0.0) + pixelOffset[2] = w1.z * (0.001 + pow(AdjustDisplacement(TexLandDisplacement2Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[2]).x, params[2]), blendPower)); + [branch] if ((PBRFlags & TruePBR_LandTile3HasDisplacement) != 0) if (w1.w > 0.0) + pixelOffset[3] = w1.w * (0.001 + pow(AdjustDisplacement(TexLandDisplacement3Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[3]).x, params[3]), blendPower)); + [branch] if ((PBRFlags & TruePBR_LandTile4HasDisplacement) != 0) if (w2.x > 0.0) + pixelOffset[4] = w2.x * (0.001 + pow(AdjustDisplacement(TexLandDisplacement4Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[4]).x, params[4]), blendPower)); + [branch] if ((PBRFlags & TruePBR_LandTile5HasDisplacement) != 0) if (w2.y > 0.0) + pixelOffset[5] = w2.y * (0.001 + pow(AdjustDisplacement(TexLandDisplacement5Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[5]).x, params[5]), blendPower)); +# else if (w1.x > 0.0) pixelOffset[0] = w1.x * (0.001 + pow(TexColorSampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[0]).w, blendPower)); if (w1.y > 0.0) @@ -60,6 +91,8 @@ float GetTerrainHeight(PS_INPUT input, float2 coords, float mipLevels[6], float pixelOffset[4] = w2.x * (0.001 + pow(TexLandColor5Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[4]).w, blendPower)); if (w2.y > 0.0) pixelOffset[5] = w2.y * (0.001 + pow(TexLandColor6Sampler.SampleLevel(SampTerrainParallaxSampler, coords, mipLevels[5]).w, blendPower)); +# endif + float total = 0; [unroll] for (int i = 0; i < 6; i++) { @@ -75,9 +108,9 @@ float GetTerrainHeight(PS_INPUT input, float2 coords, float mipLevels[6], float #endif #if defined(LANDSCAPE) -float2 GetParallaxCoords(PS_INPUT input, float distance, float2 coords, float mipLevels[6], float3 viewDir, float3x3 tbn, float noise, out float pixelOffset, out float heights[6]) +float2 GetParallaxCoords(PS_INPUT input, float distance, float2 coords, float mipLevels[6], float3 viewDir, float3x3 tbn, float noise, DisplacementParams params[6], out float pixelOffset, out float heights[6]) #else -float2 GetParallaxCoords(float distance, float2 coords, float mipLevel, float3 viewDir, float3x3 tbn, float noise, Texture2D tex, SamplerState texSampler, uint channel, out float pixelOffset) +float2 GetParallaxCoords(float distance, float2 coords, float mipLevel, float3 viewDir, float3x3 tbn, float noise, Texture2D tex, SamplerState texSampler, uint channel, DisplacementParams params, out float pixelOffset) #endif { float3 viewDirTS = normalize(mul(tbn, viewDir)); @@ -89,7 +122,11 @@ float2 GetParallaxCoords(float distance, float2 coords, float mipLevel, float3 v float blendFactor = extendedMaterialSettings.EnableComplexMaterial ? saturate(1 - nearBlendToFar) : INV_HEIGHT_POWER; #endif - float maxHeight = 0.1; +#if defined(LANDSCAPE) + float maxHeight = 0.1 * (params[0].HeightScale + params[1].HeightScale + params[2].HeightScale + params[3].HeightScale + params[4].HeightScale + params[5].HeightScale) / 6; +#else + float maxHeight = 0.1 * params.HeightScale; +#endif float minHeight = maxHeight * 0.5; if (nearBlendToFar < 1.0) { @@ -116,16 +153,18 @@ float2 GetParallaxCoords(float distance, float2 coords, float mipLevel, float3 v #if defined(LANDSCAPE) float4 currHeight; - currHeight.x = GetTerrainHeight(input, currentOffset[0].xy, mipLevels, blendFactor, heights); - currHeight.y = GetTerrainHeight(input, currentOffset[0].zw, mipLevels, blendFactor, heights); - currHeight.z = GetTerrainHeight(input, currentOffset[1].xy, mipLevels, blendFactor, heights); - currHeight.w = GetTerrainHeight(input, currentOffset[1].zw, mipLevels, blendFactor, heights); + currHeight.x = GetTerrainHeight(input, currentOffset[0].xy, mipLevels, params, blendFactor, heights); + currHeight.y = GetTerrainHeight(input, currentOffset[0].zw, mipLevels, params, blendFactor, heights); + currHeight.z = GetTerrainHeight(input, currentOffset[1].xy, mipLevels, params, blendFactor, heights); + currHeight.w = GetTerrainHeight(input, currentOffset[1].zw, mipLevels, params, blendFactor, heights); #else float4 currHeight; currHeight.x = tex.SampleLevel(texSampler, currentOffset[0].xy, mipLevel)[channel]; currHeight.y = tex.SampleLevel(texSampler, currentOffset[0].zw, mipLevel)[channel]; currHeight.z = tex.SampleLevel(texSampler, currentOffset[1].xy, mipLevel)[channel]; currHeight.w = tex.SampleLevel(texSampler, currentOffset[1].zw, mipLevel)[channel]; + + currHeight = AdjustDisplacement(currHeight, params); #endif bool4 testResult = currHeight >= currentBound; @@ -197,34 +236,34 @@ float2 GetParallaxCoords(float distance, float2 coords, float mipLevel, float3 v // https://advances.realtimerendering.com/s2006/Tatarchuk-POM.pdf // Cheap method of creating shadows using height for a given light source -float GetParallaxSoftShadowMultiplier(float2 coords, float mipLevel, float3 L, float sh0, Texture2D tex, SamplerState texSampler, uint channel, float quality, float noise) +float GetParallaxSoftShadowMultiplier(float2 coords, float mipLevel, float3 L, float sh0, Texture2D tex, SamplerState texSampler, uint channel, float quality, float noise, DisplacementParams params) { [branch] if (quality > 0.0) { float2 rayDir = L.xy * 0.1; float4 multipliers = rcp((float4(4, 3, 2, 1) + noise)); float4 sh; - sh.x = tex.SampleLevel(texSampler, coords + rayDir * multipliers.x, mipLevel)[channel]; - sh.y = tex.SampleLevel(texSampler, coords + rayDir * multipliers.y, mipLevel)[channel]; - sh.z = tex.SampleLevel(texSampler, coords + rayDir * multipliers.z, mipLevel)[channel]; - sh.w = tex.SampleLevel(texSampler, coords + rayDir * multipliers.w, mipLevel)[channel]; - return 1.0 - saturate(dot(max(0, sh - sh0), 1.0) * 2.0) * quality; + sh.x = AdjustDisplacement(tex.SampleLevel(texSampler, coords + rayDir * multipliers.x, mipLevel)[channel], params); + sh.y = AdjustDisplacement(tex.SampleLevel(texSampler, coords + rayDir * multipliers.y, mipLevel)[channel], params); + sh.z = AdjustDisplacement(tex.SampleLevel(texSampler, coords + rayDir * multipliers.z, mipLevel)[channel], params); + sh.w = AdjustDisplacement(tex.SampleLevel(texSampler, coords + rayDir * multipliers.w, mipLevel)[channel], params); + return 1.0 - saturate(dot(max(0, sh - sh0), 1.0) * params.HeightScale * 2.0) * quality; } return 1.0; } #if defined(LANDSCAPE) -float GetParallaxSoftShadowMultiplierTerrain(PS_INPUT input, float2 coords, float mipLevel[6], float3 L, float sh0, float quality, float noise) +float GetParallaxSoftShadowMultiplierTerrain(PS_INPUT input, float2 coords, float mipLevel[6], float3 L, float sh0, float quality, float noise, DisplacementParams params[6]) { if (quality > 0.0) { float2 rayDir = L.xy * 0.1; float4 multipliers = rcp((float4(4, 3, 2, 1) + noise)); float4 sh; float heights[6] = { 0, 0, 0, 0, 0, 0 }; - sh.x = GetTerrainHeight(input, coords + rayDir * multipliers.x, mipLevel, quality, heights); - sh.y = GetTerrainHeight(input, coords + rayDir * multipliers.y, mipLevel, quality, heights); - sh.z = GetTerrainHeight(input, coords + rayDir * multipliers.z, mipLevel, quality, heights); - sh.w = GetTerrainHeight(input, coords + rayDir * multipliers.w, mipLevel, quality, heights); + sh.x = GetTerrainHeight(input, coords + rayDir * multipliers.x, mipLevel, params, quality, heights); + sh.y = GetTerrainHeight(input, coords + rayDir * multipliers.y, mipLevel, params, quality, heights); + sh.z = GetTerrainHeight(input, coords + rayDir * multipliers.z, mipLevel, params, quality, heights); + sh.w = GetTerrainHeight(input, coords + rayDir * multipliers.w, mipLevel, params, quality, heights); return 1.0 - saturate(dot(max(0, sh - sh0), 1.0) * 2.0) * quality; } return 1.0; diff --git a/features/Grass Lighting/Shaders/RunGrass.hlsl b/features/Grass Lighting/Shaders/RunGrass.hlsl index e4e5c5511..cd86502a1 100644 --- a/features/Grass Lighting/Shaders/RunGrass.hlsl +++ b/features/Grass Lighting/Shaders/RunGrass.hlsl @@ -214,7 +214,13 @@ struct PS_OUTPUT float4 NormalGlossiness : SV_Target2; float4 Albedo : SV_Target3; float4 Specular : SV_Target4; +# if defined(TRUE_PBR) + float4 Reflectance : SV_Target5; +# endif float4 Masks : SV_Target6; +# if defined(TRUE_PBR) + float4 Parameters : SV_Target7; +# endif #endif // RENDER_DEPTH }; @@ -222,9 +228,21 @@ struct PS_OUTPUT SamplerState SampBaseSampler : register(s0); SamplerState SampShadowMaskSampler : register(s1); +# if defined(TRUE_PBR) +SamplerState SampNormalSampler : register(s2); +SamplerState SampRMAOSSampler : register(s3); +SamplerState SampSubsurfaceSampler : register(s4); +# endif + Texture2D TexBaseSampler : register(t0); Texture2D TexShadowMaskSampler : register(t1); +# if defined(TRUE_PBR) +Texture2D TexNormalSampler : register(t2); +Texture2D TexRMAOSSampler : register(t3); +Texture2D TexSubsurfaceSampler : register(t4); +# endif + cbuffer PerFrame : register(b0) { float4 cb0_1[2] : packoffset(c0); @@ -239,6 +257,15 @@ cbuffer AlphaTestRefCB : register(b11) } # endif // VR +# if defined(TRUE_PBR) +cbuffer PerMaterial : register(b1) +{ + uint PBRFlags : packoffset(c0.x); + float3 PBRParams1 : packoffset(c0.y); // roughness scale, specular level + float4 PBRParams2 : packoffset(c1); // subsurface color, subsurface opacity +}; +# endif + float3 GetLightSpecularInput(float3 L, float3 V, float3 N, float3 lightColor, float shininess) { float3 H = normalize(V + L); @@ -301,21 +328,30 @@ float3x3 CalculateTBN(float3 N, float3 p, float2 uv) # include "Skylighting/Skylighting.hlsli" # endif +# if defined(TRUE_PBR) +# include "Common/PBR.hlsli" +# endif + PS_OUTPUT main(PS_INPUT input, bool frontFace : SV_IsFrontFace) { PS_OUTPUT psout; +# if !defined(TRUE_PBR) float x; float y; TexBaseSampler.GetDimensions(x, y); bool complex = x != y; +# endif float4 baseColor; +# if !defined(TRUE_PBR) if (complex) { baseColor = TexBaseSampler.Sample(SampBaseSampler, float2(input.TexCoord.x, input.TexCoord.y * 0.5)); - } else { + } else +# endif + { baseColor = TexBaseSampler.Sample(SampBaseSampler, input.TexCoord.xy); } @@ -331,7 +367,11 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace psout.PS.xyz = input.Depth.xxx / input.Depth.yyy; psout.PS.w = diffuseAlpha; # else - float4 specColor = complex ? TexBaseSampler.Sample(SampBaseSampler, float2(input.TexCoord.x, 0.5 + input.TexCoord.y * 0.5)) : 0; +# if !defined(TRUE_PBR) + float4 specColor = complex ? TexBaseSampler.Sample(SampBaseSampler, float2(input.TexCoord.x, 0.5 + input.TexCoord.y * 0.5)) : 1; +# else + float4 specColor = TexNormalSampler.Sample(SampNormalSampler, input.TexCoord.xy); +# endif float4 shadowColor = TexShadowMaskSampler.Load(int3(input.HPosition.xy, 0)); uint eyeIndex = GetEyeIndexPS(input.HPosition, VPOSOffset); @@ -350,18 +390,62 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace normal = normalize(lerp(normal, normalize(input.SphereNormal.xyz), input.SphereNormal.w)); - if (complex) { +# if !defined(TRUE_PBR) + if (complex) +# endif + { float3 normalColor = TransformNormal(specColor.xyz); // world-space -> tangent-space -> world-space. // This is because we don't have pre-computed tangents. normal = normalize(mul(normalColor, CalculateTBN(normal, -input.WorldPosition.xyz, input.TexCoord.xy))); } +# if !defined(TRUE_PBR) if (!complex || grassLightingSettings.OverrideComplexGrassSettings) baseColor.xyz *= grassLightingSettings.BasicGrassBrightness; +# endif + +# if defined(TRUE_PBR) + float4 rawRMAOS = TexRMAOSSampler.Sample(SampRMAOSSampler, input.TexCoord.xy) * float4(PBRParams1.x, 1, 1, PBRParams1.y); + + PBRSurfaceProperties pbrSurfaceProperties; + + pbrSurfaceProperties.Roughness = saturate(rawRMAOS.x); + pbrSurfaceProperties.Metallic = saturate(rawRMAOS.y); + pbrSurfaceProperties.AO = rawRMAOS.z; + pbrSurfaceProperties.F0 = lerp(saturate(rawRMAOS.w), baseColor.xyz, pbrSurfaceProperties.Metallic); + + pbrSurfaceProperties.SubsurfaceColor = 0; + pbrSurfaceProperties.Thickness = 0; + + pbrSurfaceProperties.CoatColor = 0; + pbrSurfaceProperties.CoatStrength = 0; + pbrSurfaceProperties.CoatRoughness = 0; + pbrSurfaceProperties.CoatF0 = 0.04; + + pbrSurfaceProperties.FuzzColor = 0; + pbrSurfaceProperties.FuzzWeight = 0; + + baseColor.xyz *= 1 - pbrSurfaceProperties.Metallic; + + pbrSurfaceProperties.BaseColor = baseColor.xyz; + + pbrSurfaceProperties.SubsurfaceColor = PBRParams2.xyz; + pbrSurfaceProperties.Thickness = PBRParams2.w; + [branch] if ((PBRFlags & TruePBR_HasFeatureTexture0) != 0) + { + float4 sampledSubsurfaceProperties = TexSubsurfaceSampler.Sample(SampSubsurfaceSampler, input.TexCoord.xy); + pbrSurfaceProperties.SubsurfaceColor *= sampledSubsurfaceProperties.xyz; + pbrSurfaceProperties.Thickness *= sampledSubsurfaceProperties.w; + } + + float3 specularColorPBR = 0; + float3 transmissionColor = 0; +# endif // TRUE_PBR float3 dirLightColor = DirLightColorShared.xyz; - dirLightColor *= shadowColor.x; + float3 dirLightColorMultiplier = 1; + dirLightColorMultiplier *= shadowColor.x; float dirLightAngle = dot(normal, DirLightDirectionShared.xyz); @@ -394,9 +478,24 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 diffuseColor = 0; float3 specularColor = 0; - float3 lightsDiffuseColor = dirLightColor * saturate(dirLightAngle) * dirShadow; + float3 lightsDiffuseColor = 0; float3 lightsSpecularColor = 0; + dirLightColor *= dirLightColorMultiplier; + +# if defined(TRUE_PBR) + { + float3 pbrDirLightColor = AdjustDirectionalLightColorForPBR(DirLightColorShared.xyz) * dirLightColorMultiplier * dirShadow; + + float3 dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor; + GetDirectLightInputPBR(dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor, normal, normal, viewDirection, viewDirection, DirLightDirection, DirLightDirection, pbrDirLightColor, pbrDirLightColor, pbrSurfaceProperties); + lightsDiffuseColor += dirDiffuseColor; + transmissionColor += dirTransmissionColor; + specularColorPBR += dirSpecularColor; + } +# else + lightsDiffuseColor += dirLightColor * saturate(dirLightAngle) * dirShadow; + float3 albedo = max(0, baseColor.xyz * input.VertexColor.xyz); float3 subsurfaceColor = lerp(RGBToLuminance(albedo.xyz), albedo.xyz, 2.0) * input.SphereNormal.w; @@ -405,6 +504,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (complex) lightsSpecularColor += GetLightSpecularInput(DirLightDirection, viewDirection, normal, dirLightColor, grassLightingSettings.Glossiness); +# endif # if defined(LIGHT_LIMIT_FIX) uint clusterIndex = 0; @@ -440,6 +540,16 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace else if (lightLimitFixSettings.EnableContactShadows) lightColor *= ContactShadows(viewPosition, screenUV, screenNoise, normalizedLightDirectionVS, shadowQualityScale, 0.0, eyeIndex); +# if defined(TRUE_PBR) + { + float3 pointDiffuseColor, coatDirDiffuseColor, pointTransmissionColor, pointSpecularColor; + float3 pbrLightColor = AdjustPointLightColorForPBR(lightColor * intensityMultiplier); + GetDirectLightInputPBR(pointDiffuseColor, coatDirDiffuseColor, pointTransmissionColor, pointSpecularColor, normal, normal, viewDirection, viewDirection, normalizedLightDirection, normalizedLightDirection, pbrLightColor, pbrLightColor, pbrSurfaceProperties); + lightsDiffuseColor += pointDiffuseColor; + transmissionColor += pointTransmissionColor; + specularColorPBR += pointSpecularColor; + } +# else float3 lightDiffuseColor = lightColor * saturate(lightAngle.xxx); sss += lightColor * saturate(-lightAngle); @@ -448,6 +558,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (complex) lightsSpecularColor += GetLightSpecularInput(normalizedLightDirection, viewDirection, normal, lightColor, grassLightingSettings.Glossiness) * intensityMultiplier; +# endif } } } @@ -455,31 +566,42 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace diffuseColor += lightsDiffuseColor; -# if !defined(SSGI) +# if defined(TRUE_PBR) + float3 indirectDiffuseLobeWeight, indirectSpecularLobeWeight; + GetPBRIndirectLobeWeights(indirectDiffuseLobeWeight, indirectSpecularLobeWeight, normal, viewDirection, baseColor.xyz, pbrSurfaceProperties); + + diffuseColor.xyz += transmissionColor; + specularColor.xyz += specularColorPBR; + specularColor.xyz = Lin2sRGB(specularColor.xyz); + diffuseColor.xyz = Lin2sRGB(diffuseColor.xyz); +# else + +# if !defined(SSGI) float3 directionalAmbientColor = mul(DirectionalAmbientShared, float4(normal, 1.0)); -# if defined(SKYLIGHTING) -# if defined(VR) +# if defined(SKYLIGHTING) +# if defined(VR) float3 positionMSSkylight = input.WorldPosition.xyz + (eyeIndex == 1 ? CameraPosAdjust[1] - CameraPosAdjust[0] : 0); -# else +# else float3 positionMSSkylight = input.WorldPosition.xyz; -# endif +# endif sh2 skylightingSH = sampleSkylighting(skylightingSettings, SkylightingProbeArray, positionMSSkylight, worldSpaceNormal); float skylighting = shHallucinateZH3Irradiance(skylightingSH, normalWS); skylighting = lerp(skylightingSettings.MixParams.x, 1, saturate(skylighting * skylightingSettings.MixParams.y)); skylighting = applySkylightingFadeout(skylighting, length(positionMSSkylight)); directionalAmbientColor *= skylighting; -# endif +# endif diffuseColor += directionalAmbientColor; -# endif +# endif diffuseColor *= albedo; diffuseColor += max(0, sss * subsurfaceColor * grassLightingSettings.SubsurfaceScatteringAmount); specularColor += lightsSpecularColor; specularColor *= specColor.w * grassLightingSettings.SpecularStrength; +# endif # if defined(LIGHT_LIMIT_FIX) && defined(LLFDEBUG) if (lightLimitFixSettings.EnableLightsVisualisation) { @@ -497,13 +619,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace psout.Diffuse.xyz = float4(diffuseColor, 1); # endif - psout.Specular = float4(specularColor, 1); - psout.Albedo = float4(albedo, 1); - psout.Masks = float4(0, 0, 0, 0); - float3 normalVS = normalize(WorldToView(normal, false, eyeIndex)); +# if defined(TRUE_PBR) + psout.Albedo = float4(Lin2sRGB(indirectDiffuseLobeWeight), 1); + psout.NormalGlossiness = float4(EncodeNormal(normalVS), 1 - pbrSurfaceProperties.Roughness, 1); + psout.Reflectance = float4(indirectSpecularLobeWeight, 1); + psout.Parameters = float4(0, 0, 1, 1); +# else + psout.Albedo = float4(albedo, 1); psout.NormalGlossiness = float4(EncodeNormal(normalVS), specColor.w, 1); +# endif + + psout.Specular = float4(specularColor, 1); + psout.Masks = float4(0, 0, 0, 0); # endif // RENDER_DEPTH return psout; } -#endif // PSHADER +#endif // PSHADER \ No newline at end of file diff --git a/include/BSLightingShaderMaterialPBR.h b/include/BSLightingShaderMaterialPBR.h new file mode 100644 index 000000000..8735afe85 --- /dev/null +++ b/include/BSLightingShaderMaterialPBR.h @@ -0,0 +1,104 @@ +#pragma once + +enum class PBRFlags : uint32_t +{ + Subsurface = 1 << 0, + TwoLayer = 1 << 1, + ColoredCoat = 1 << 2, + InterlayerParallax = 1 << 3, + CoatNormal = 1 << 4, + Fuzz = 1 << 5, + HairMarschner = 1 << 6, +}; + +enum class PBRShaderFlags : uint32_t +{ + HasEmissive = 1 << 0, + HasDisplacement = 1 << 1, + HasFeaturesTexture0 = 1 << 2, + HasFeaturesTexture1 = 1 << 3, + Subsurface = 1 << 4, + TwoLayer = 1 << 5, + ColoredCoat = 1 << 6, + InterlayerParallax = 1 << 7, + CoatNormal = 1 << 8, + Fuzz = 1 << 9, + HairMarschner = 1 << 10, +}; + +class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase +{ +public: + inline static constexpr auto FEATURE = static_cast(32); + + inline static constexpr auto RmaosTexture = static_cast(5); + inline static constexpr auto EmissiveTexture = static_cast(2); + inline static constexpr auto DisplacementTexture = static_cast(3); + inline static constexpr auto FeaturesTexture0 = static_cast(7); + inline static constexpr auto FeaturesTexture1 = static_cast(6); + + ~BSLightingShaderMaterialPBR(); + + // override (BSLightingShaderMaterialBase) + RE::BSShaderMaterial* Create() override; // 01 + void CopyMembers(RE::BSShaderMaterial* that) override; // 02 + std::uint32_t ComputeCRC32(uint32_t srcHash) override; // 04 + Feature GetFeature() const override; // 06 + void OnLoadTextureSet(std::uint64_t arg1, RE::BSTextureSet* inTextureSet) override; // 08 + void ClearTextures() override; // 09 + void ReceiveValuesFromRootMaterial(bool skinned, bool rimLighting, bool softLighting, bool backLighting, bool MSN) override; // 0A + uint32_t GetTextures(RE::NiSourceTexture** textures) override; // 0B + void LoadBinary(RE::NiStream& stream) override; // 0D + + static BSLightingShaderMaterialPBR* Make(); + + float GetRoughnessScale() const; + float GetSpecularLevel() const; + + float GetDisplacementScale() const; + + const RE::NiColor& GetSubsurfaceColor() const; + float GetSubsurfaceOpacity() const; + + const RE::NiColor& GetCoatColor() const; + float GetCoatStrength() const; + float GetCoatRoughness() const; + float GetCoatSpecularLevel() const; + + const std::array& GetProjectedMaterialBaseColorScale() const; + float GetProjectedMaterialRoughness() const; + float GetProjectedMaterialSpecularLevel() const; + + const RE::NiColor& GetFuzzColor() const; + float GetFuzzWeight() const; + + // members + RE::BSShaderMaterial::Feature loadedWithFeature = RE::BSShaderMaterial::Feature::kDefault; + + stl::enumeration pbrFlags; + + float coatRoughness = 1.f; + float coatSpecularLevel = 0.04f; + + RE::NiColor fuzzColor; + float fuzzWeight = 0.f; + + std::array projectedMaterialBaseColorScale = { 1.f, 1.f, 1.f }; + float projectedMaterialRoughness = 1.f; + float projectedMaterialSpecularLevel = 0.04f; + + // Roughness in r, metallic in g, AO in b, nonmetal reflectance in a + RE::NiPointer rmaosTexture; + + // Emission color in rgb + RE::NiPointer emissiveTexture; + + // Displacement in r + RE::NiPointer displacementTexture; + + // Subsurface map (subsurface color in rgb, thickness in a) / Coat map (coat color in rgb, coat strength in a) + RE::NiPointer featuresTexture0; + + // Fuzz map (fuzz color in rgb, fuzz weight in a) / Coat normal map (coat normal in rgb, coat roughness in a) + RE::NiPointer featuresTexture1; +}; diff --git a/include/BSLightingShaderMaterialPBRLandscape.h b/include/BSLightingShaderMaterialPBRLandscape.h new file mode 100644 index 000000000..46ad87873 --- /dev/null +++ b/include/BSLightingShaderMaterialPBRLandscape.h @@ -0,0 +1,44 @@ +#pragma once + +class BSLightingShaderMaterialPBRLandscape : public RE::BSLightingShaderMaterialBase +{ +public: + inline static constexpr auto FEATURE = static_cast(33); + + inline static constexpr auto BaseColorTexture = static_cast(0); + inline static constexpr auto NormalTexture = static_cast(1); + inline static constexpr auto DisplacementTexture = static_cast(3); + inline static constexpr auto RmaosTexture = static_cast(5); + + inline static constexpr uint32_t NumTiles = 6; + + BSLightingShaderMaterialPBRLandscape(); + ~BSLightingShaderMaterialPBRLandscape(); + + // override (BSLightingShaderMaterialBase) + RE::BSShaderMaterial* Create() override; // 01 + void CopyMembers(RE::BSShaderMaterial* that) override; // 02 + Feature GetFeature() const override; // 06 + void ClearTextures() override; // 09 + void ReceiveValuesFromRootMaterial(bool skinned, bool rimLighting, bool softLighting, bool backLighting, bool MSN) override; // 0A + uint32_t GetTextures(RE::NiSourceTexture** textures) override; // 0B + + static BSLightingShaderMaterialPBRLandscape* Make(); + + // members + std::uint32_t numLandscapeTextures = 0; + RE::NiPointer landscapeBaseColorTextures[NumTiles - 1]; + RE::NiPointer landscapeNormalTextures[NumTiles - 1]; + RE::NiPointer terrainOverlayTexture; + RE::NiPointer terrainNoiseTexture; + RE::NiColorA landBlendParams; + std::array, NumTiles> landscapeDisplacementTextures; + std::array, NumTiles> landscapeRMAOSTextures; + std::array isPbr; + std::array roughnessScales; + std::array displacementScales; + std::array specularLevels; + float terrainTexOffsetX = 0.f; + float terrainTexOffsetY = 0.f; + float terrainTexFade = 0.f; +}; diff --git a/package/Shaders/AmbientCompositeCS.hlsl b/package/Shaders/AmbientCompositeCS.hlsl index 6e3abb19f..9d532ae9f 100644 --- a/package/Shaders/AmbientCompositeCS.hlsl +++ b/package/Shaders/AmbientCompositeCS.hlsl @@ -25,6 +25,8 @@ Texture3D SkylightingProbeArray : register(t3); Texture2D SSGITexture : register(t4); #endif +Texture2D Masks2Texture : register(t5); + RWTexture2D MainRW : register(u0); #if defined(SSGI) RWTexture2D DiffuseAmbientRW : register(u1); @@ -40,17 +42,20 @@ RWTexture2D DiffuseAmbientRW : register(u1); half3 diffuseColor = MainRW[dispatchID.xy]; half3 albedo = AlbedoTexture[dispatchID.xy]; + half3 masks2 = Masks2Texture[dispatchID.xy]; + + half pbrWeight = masks2.z; half3 normalWS = normalize(mul(CameraViewInverse[eyeIndex], half4(normalVS, 0)).xyz); half3 directionalAmbientColor = mul(DirectionalAmbient, half4(normalWS, 1.0)); - half3 ambient = albedo * directionalAmbientColor; + half3 linAlbedo = sRGB2Lin(albedo); + half3 linDirectionalAmbientColor = sRGB2Lin(directionalAmbientColor); + half3 linDiffuseColor = sRGB2Lin(diffuseColor); - diffuseColor = sRGB2Lin(diffuseColor); - - ambient = sRGB2Lin(max(0, ambient)); // Fixes black blobs on the world map - albedo = sRGB2Lin(albedo); + half3 linAmbient = lerp(sRGB2Lin(albedo * directionalAmbientColor), linAlbedo * linDirectionalAmbientColor, pbrWeight); + linAmbient = max(0, linAmbient); // Fixes black blobs on the world map half visibility = 1.0; #if defined(SKYLIGHTING) @@ -73,22 +78,24 @@ RWTexture2D DiffuseAmbientRW : register(u1); #if defined(SSGI) half4 ssgiDiffuse = SSGITexture[dispatchID.xy]; - ssgiDiffuse.rgb *= albedo; + ssgiDiffuse.rgb *= linAlbedo; visibility = min(visibility, ssgiDiffuse.a); - DiffuseAmbientRW[dispatchID.xy] = ambient + ssgiDiffuse.rgb; + DiffuseAmbientRW[dispatchID.xy] = linAmbient + ssgiDiffuse.rgb; # if defined(INTERIOR) - diffuseColor *= ssgiDiffuse.a; + linDiffuseColor *= ssgiDiffuse.a; # endif - diffuseColor += ssgiDiffuse.rgb; + linDiffuseColor += ssgiDiffuse.rgb; #endif - ambient = Lin2sRGB(ambient * visibility); - diffuseColor = Lin2sRGB(diffuseColor); + linAmbient *= visibility; + + half3 ambient = Lin2sRGB(linAmbient); + diffuseColor = Lin2sRGB(linDiffuseColor); - diffuseColor += ambient; + diffuseColor = lerp(diffuseColor + ambient, Lin2sRGB(linDiffuseColor + linAmbient), pbrWeight); MainRW[dispatchID.xy] = diffuseColor; }; \ No newline at end of file diff --git a/package/Shaders/Common/PBR.hlsli b/package/Shaders/Common/PBR.hlsli new file mode 100644 index 000000000..9acca1a95 --- /dev/null +++ b/package/Shaders/Common/PBR.hlsli @@ -0,0 +1,478 @@ +struct PBRSurfaceProperties +{ + float3 BaseColor; + float Roughness; + float Metallic; + float AO; + float3 F0; + float3 SubsurfaceColor; + float Thickness; + float3 CoatColor; + float CoatStrength; + float CoatRoughness; + float3 CoatF0; + float3 FuzzColor; + float FuzzWeight; +}; + +#define TruePBR_HasEmissive (1 << 0) +#define TruePBR_HasDisplacement (1 << 1) +#define TruePBR_HasFeatureTexture0 (1 << 2) +#define TruePBR_HasFeatureTexture1 (1 << 3) +#define TruePBR_Subsurface (1 << 4) +#define TruePBR_TwoLayer (1 << 5) +#define TruePBR_ColoredCoat (1 << 6) +#define TruePBR_InterlayerParallax (1 << 7) +#define TruePBR_CoatNormal (1 << 8) +#define TruePBR_Fuzz (1 << 9) +#define TruePBR_HairMarschner (1 << 9) +#define TruePBR_LandTile0PBR (1 << 0) +#define TruePBR_LandTile1PBR (1 << 1) +#define TruePBR_LandTile2PBR (1 << 2) +#define TruePBR_LandTile3PBR (1 << 3) +#define TruePBR_LandTile4PBR (1 << 4) +#define TruePBR_LandTile5PBR (1 << 5) +#define TruePBR_LandTile0HasDisplacement (1 << 6) +#define TruePBR_LandTile1HasDisplacement (1 << 7) +#define TruePBR_LandTile2HasDisplacement (1 << 8) +#define TruePBR_LandTile3HasDisplacement (1 << 9) +#define TruePBR_LandTile4HasDisplacement (1 << 10) +#define TruePBR_LandTile5HasDisplacement (1 << 11) + +float3 AdjustDirectionalLightColorForPBR(float3 lightColor) +{ + return pbrSettings.DirectionalLightColorMultiplier * sRGB2Lin(lightColor); +} + +float3 AdjustPointLightColorForPBR(float3 lightColor) +{ + return pbrSettings.PointLightColorMultiplier * sRGB2Lin(lightColor); +} + +float3 AdjustAmbientLightColorForPBR(float3 lightColor) +{ + return pbrSettings.AmbientLightColorMultiplier * sRGB2Lin(lightColor); +} + +// [Jimenez et al. 2016, "Practical Realtime Strategies for Accurate Indirect Occlusion"] +float3 MultiBounceAO(float3 baseColor, float ao) +{ + float3 a = 2.0404 * baseColor - 0.3324; + float3 b = -4.7951 * baseColor + 0.6417; + float3 c = 2.7552 * baseColor + 0.6903; + return max(ao, ((ao * a + b) * ao + c) * ao); +} + +// [Lagarde et al. 2014, "Moving Frostbite to Physically Based Rendering 3.0"] +float SpecularAOLagarde(float NdotV, float ao, float roughness) +{ + return saturate(pow(NdotV + ao, exp2(-16.0 * roughness - 1.0)) - 1.0 + ao); +} + +// [Schlick 1994, "An Inexpensive BRDF Model for Physically-Based Rendering"] +float3 GetFresnelFactorSchlick(float3 specularColor, float VdotH) +{ + float Fc = pow(1 - VdotH, 5); + return Fc + (1 - Fc) * specularColor; +} + +// [Heitz 2014, "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"] +float GetVisibilityFunctionSmithJointApprox(float roughness, float NdotV, float NdotL) +{ + float a = roughness * roughness; + float visSmithV = NdotL * (NdotV * (1 - a) + a); + float visSmithL = NdotV * (NdotL * (1 - a) + a); + return 0.5 * rcp(visSmithV + visSmithL); +} + +// [Neubelt et al. 2013, "Crafting a Next-gen Material Pipeline for The Order: 1886"] +float GetVisibilityFunctionNeubelt(float NdotV, float NdotL) +{ + return rcp(4 * (NdotL + NdotV - NdotL * NdotV)); +} + +// [Walter et al. 2007, "Microfacet models for refraction through rough surfaces"] +float GetNormalDistributionFunctionGGX(float roughness, float NdotH) +{ + float a2 = pow(roughness, 4); + float d = max((NdotH * a2 - NdotH) * NdotH + 1, 1e-5); + return a2 / (PI * d * d); +} + +// [Estevez et al. 2017, "Production Friendly Microfacet Sheen BRDF"] +float GetNormalDistributionFunctionCharlie(float roughness, float NdotH) +{ + float invAlpha = pow(roughness, -4); + float cos2h = NdotH * NdotH; + float sin2h = max(1.0 - cos2h, 1e-5); + return (2.0 + invAlpha) * pow(sin2h, invAlpha * 0.5) / (2.0 * PI); +} + +float3 GetSpecularDirectLightMultiplierMicrofacet(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH, out float3 F) +{ + float D = GetNormalDistributionFunctionGGX(roughness, NdotH); + float G = GetVisibilityFunctionSmithJointApprox(roughness, NdotV, NdotL); + F = GetFresnelFactorSchlick(specularColor, VdotH); + + return D * G * F; +} + +float3 GetSpecularDirectLightMultiplierMicroflakes(float roughness, float3 specularColor, float NdotL, float NdotV, float NdotH, float VdotH) +{ + float D = GetNormalDistributionFunctionCharlie(roughness, NdotH); + float G = GetVisibilityFunctionNeubelt(NdotV, NdotL); + float3 F = GetFresnelFactorSchlick(specularColor, VdotH); + + return D * G * F; +} + +float GetDiffuseDirectLightMultiplierLambert() +{ + return 1 / PI; +} + +// [Burley 2012, "Physically-Based Shading at Disney"] +float3 GetDiffuseDirectLightMultiplierBurley(float roughness, float NdotV, float NdotL, float VdotH) +{ + float Fd90 = 0.5 + 2 * VdotH * VdotH * roughness; + float FdV = 1 + (Fd90 - 1) * pow(1 - NdotV, 5); + float FdL = 1 + (Fd90 - 1) * pow(1 - NdotL, 5); + return (1 / PI) * FdV * FdL; +} + +// [Oren et al. 1994, "Generalization of Lambert’s Reflectance Model"] +float3 GetDiffuseDirectLightMultiplierOrenNayar(float roughness, float3 N, float3 V, float3 L, float NdotV, float NdotL) +{ + float a = roughness * roughness * 0.25; + float A = 1.0 - 0.5 * (a / (a + 0.33)); + float B = 0.45 * (a / (a + 0.09)); + + float gamma = dot(V - N * NdotV, L - N * NdotL) / (sqrt(saturate(1.0 - NdotV * NdotV)) * sqrt(saturate(1.0 - NdotL * NdotL))); + + float2 cos_alpha_beta = NdotV < NdotL ? float2(NdotV, NdotL) : float2(NdotL, NdotV); + float2 sin_alpha_beta = sqrt(saturate(1.0 - cos_alpha_beta * cos_alpha_beta)); + float C = sin_alpha_beta.x * sin_alpha_beta.y / (1e-6 + cos_alpha_beta.y); + + return (1 / PI) * (A + B * max(0.0, gamma) * C); +} + +// [Gotanda 2014, "Designing Reflectance Models for New Consoles"] +float3 GetDiffuseDirectLightMultiplierGotanda(float roughness, float NdotV, float NdotL, float VdotL) +{ + float a = roughness * roughness; + float a2 = a * a; + float F0 = 0.04; + float Cosri = VdotL - NdotV * NdotL; + float Fr = (1 - (0.542026 * a2 + 0.303573 * a) / (a2 + 1.36053)) * (1 - pow(1 - NdotV, 5 - 4 * a2) / (a2 + 1.36053)) * ((-0.733996 * a2 * a + 1.50912 * a2 - 1.16402 * a) * pow(1 - NdotV, 1 + rcp(39 * a2 * a2 + 1)) + 1); + float Lm = (max(1 - 2 * a, 0) * (1 - pow(1 - NdotL, 5)) + min(2 * a, 1)) * (1 - 0.5 * a * (NdotL - 1)) * NdotL; + float Vd = (a2 / ((a2 + 0.09) * (1.31072 + 0.995584 * NdotV))) * (1 - pow(1 - NdotL, (1 - 0.3726732 * NdotV * NdotV) / (0.188566 + 0.38841 * NdotV))); + float Bp = Cosri < 0 ? 1.4 * NdotV * NdotL * Cosri : Cosri; + float Lr = (21.0 / 20.0) * (1 - F0) * (Fr * Lm + Vd + Bp); + return (1 / PI) * Lr; +} + +// [Chan 2018, "Material Advances in Call of Duty: WWII"] +float3 GetDiffuseDirectLightMultiplierChan(float roughness, float NdotV, float NdotL, float VdotH, float NdotH) +{ + float a = roughness * roughness; + float a2 = a * a; + float g = saturate((1.0 / 18.0) * log2(2 * rcp(a2) - 1)); + + float F0 = VdotH + pow(1 - VdotH, 5); + float FdV = 1 - 0.75 * pow(1 - NdotV, 5); + float FdL = 1 - 0.75 * pow(1 - NdotL, 5); + + float Fd = lerp(F0, FdV * FdL, saturate(2.2 * g - 0.5)); + + float Fb = ((34.5 * g - 59) * g + 24.5) * VdotH * exp2(-max(73.2 * g - 21.2, 8.9) * sqrt(NdotH)); + + return (1 / PI) * (Fd + Fb); +} + +// [Lazarov 2013, "Getting More Physical in Call of Duty: Black Ops II"] +float2 GetEnvBRDFApproxLazarov(float roughness, float NdotV) +{ + const float4 c0 = { -1, -0.0275, -0.572, 0.022 }; + const float4 c1 = { 1, 0.0425, 1.04, -0.04 }; + float4 r = roughness * c0 + c1; + float a004 = min(r.x * r.x, exp2(-9.28 * NdotV)) * r.x + r.y; + float2 AB = float2(-1.04, 1.04) * a004 + r.zw; + return AB; +} + +float HairIOR() +{ + const float n = 1.55; + const float a = 1; + + float ior1 = 2 * (n - 1) * (a * a) - n + 2; + float ior2 = 2 * (n - 1) / (a * a) - n + 2; + return 0.5f * ((ior1 + ior2) + 0.5f * (ior1 - ior2)); //assume cos2PhiH = 0.5f +} + +float IORToF0(float IOF) +{ + return pow((1 - IOF) / (1 + IOF), 2); +} + +inline float HairGaussian(float B, float Theta) +{ + return exp(-0.5 * Theta * Theta / (B * B)) / (sqrt(2 * PI) * B); +} + +float3 GetHairDiffuseColorMarschner(float3 N, float3 V, float3 L, float NdotL, float NdotV, float VdotL, float backlit, float area, PBRSurfaceProperties surfaceProperties) +{ + float3 S = 0; + + float cosThetaL = sqrt(max(0, 1 - NdotL * NdotL)); + float cosThetaV = sqrt(max(0, 1 - NdotV * NdotV)); + float cosThetaD = sqrt((1 + cosThetaL * cosThetaV + NdotV * NdotL) / 2.0); + + const float3 Lp = L - NdotL * N; + const float3 Vp = V - NdotL * N; + const float cosPhi = dot(Lp, Vp) * rsqrt(dot(Lp, Lp) * dot(Vp, Vp) + 1e-4); + const float cosHalfPhi = sqrt(saturate(0.5 + 0.5 * cosPhi)); + + float n_prime = 1.19 / cosThetaD + 0.36 * cosThetaD; + + const float Shift = 0.0499f; + const float Alpha[] = { + -Shift * 2, + Shift, + Shift * 4 + }; + float B[] = { + area + surfaceProperties.Roughness, + area + surfaceProperties.Roughness / 2, + area + surfaceProperties.Roughness * 2 + }; + + float hairIOR = HairIOR(); + float specularColor = IORToF0(hairIOR); + + float3 Tp; + float Mp, Np, Fp, a, h, f; + float ThetaH = NdotL + NdotV; + // R + Mp = HairGaussian(B[0], ThetaH - Alpha[0]); + Np = 0.25 * cosHalfPhi; + Fp = GetFresnelFactorSchlick(specularColor, sqrt(saturate(0.5 + 0.5 * VdotL))); + S += (Mp * Np) * (Fp * lerp(1, backlit, saturate(-VdotL))); + + // TT + Mp = HairGaussian(B[1], ThetaH - Alpha[1]); + a = (1.55f / hairIOR) * rcp(n_prime); + h = cosHalfPhi * (1 + a * (0.6 - 0.8 * cosPhi)); + f = GetFresnelFactorSchlick(specularColor, cosThetaD * sqrt(saturate(1 - h * h))); + Fp = (1 - f) * (1 - f); + Tp = pow(surfaceProperties.BaseColor, 0.5 * sqrt(1 - (h * a) * (h * a)) / cosThetaD); + Np = exp(-3.65 * cosPhi - 3.98); + S += (Mp * Np) * (Fp * Tp) * backlit; + + // TRT + Mp = HairGaussian(B[2], ThetaH - Alpha[2]); + f = GetFresnelFactorSchlick(specularColor, cosThetaD * 0.5f); + Fp = (1 - f) * (1 - f) * f; + Tp = pow(surfaceProperties.BaseColor, 0.8 / cosThetaD); + Np = exp(17 * cosPhi - 16.78); + S += (Mp * Np) * (Fp * Tp); + + return S; +} + +float3 GetHairDiffuseAttenuationKajiyaKay(float3 N, float3 V, float3 L, float NdotL, float NdotV, float shadow, PBRSurfaceProperties surfaceProperties) +{ + float3 S = 0; + + float diffuseKajiya = 1 - abs(NdotL); + + float3 fakeN = normalize(V - N * NdotV); + const float wrap = 1; + float wrappedNdotL = saturate((dot(fakeN, L) + wrap) / ((1 + wrap) * (1 + wrap))); + float diffuseScatter = (1 / PI) * lerp(wrappedNdotL, diffuseKajiya, 0.33); + float luma = RGBToLuminance(surfaceProperties.BaseColor); + float3 scatterTint = pow(surfaceProperties.BaseColor / luma, 1 - shadow); + S += sqrt(surfaceProperties.BaseColor) * diffuseScatter * scatterTint; + + return S; +} + +float3 GetHairColorMarschner(float3 N, float3 V, float3 L, float NdotL, float NdotV, float VdotL, float shadow, float backlit, float area, PBRSurfaceProperties surfaceProperties) +{ + float3 color = 0; + + color += GetHairDiffuseColorMarschner(N, V, L, NdotL, NdotV, VdotL, backlit, area, surfaceProperties); + [branch] if (pbrSettings.UseMultipleScattering) + { + color += GetHairDiffuseAttenuationKajiyaKay(N, V, L, NdotL, NdotV, shadow, surfaceProperties); + } + + return color; +} + +void GetDirectLightInputPBR(out float3 diffuse, out float3 coatDiffuse, out float3 transmission, out float3 specular, float3 N, float3 coatN, float3 V, float3 coatV, float3 L, float3 coatL, float3 lightColor, float3 coatLightColor, PBRSurfaceProperties surfaceProperties) +{ + diffuse = 0; + coatDiffuse = 0; + transmission = 0; + specular = 0; + + float3 H = normalize(V + L); + + float NdotL = dot(N, L); + float NdotV = dot(N, V); + float VdotL = dot(V, L); + float NdotH = dot(N, H); + float VdotH = dot(V, H); + + float satNdotL = clamp(NdotL, 1e-5, 1); + float satNdotV = saturate(abs(NdotV) + 1e-5); + float satVdotL = saturate(VdotL); + float satNdotH = saturate(NdotH); + float satVdotH = saturate(VdotH); + +#if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + [branch] if ((PBRFlags & TruePBR_HairMarschner) != 0) + { + transmission += PI * lightColor * GetHairColorMarschner(N, V, L, NdotL, NdotV, VdotL, 0, 1, 0, surfaceProperties); + } + else +#endif + { + diffuse += lightColor * satNdotL; + + float3 F; + specular += PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.Roughness, surfaceProperties.F0, satNdotL, satNdotV, satNdotH, satVdotH, F) * lightColor * satNdotL; + + float2 specularBRDF = 0; + [branch] if (pbrSettings.UseMultipleScattering) + { + specularBRDF = GetEnvBRDFApproxLazarov(surfaceProperties.Roughness, satNdotV); + specular *= 1 + surfaceProperties.F0 * (1 / (specularBRDF.x + specularBRDF.y) - 1); + } + +#if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + [branch] if ((PBRFlags & TruePBR_Fuzz) != 0) + { + float3 fuzzSpecular = PI * GetSpecularDirectLightMultiplierMicroflakes(surfaceProperties.Roughness, surfaceProperties.FuzzColor, satNdotL, satNdotV, satNdotH, satVdotH) * lightColor * satNdotL; + [branch] if (pbrSettings.UseMultipleScattering) + { + fuzzSpecular *= 1 + surfaceProperties.FuzzColor * (1 / (specularBRDF.x + specularBRDF.y) - 1); + } + + specular = lerp(specular, fuzzSpecular, surfaceProperties.FuzzWeight); + } + + [branch] if ((PBRFlags & TruePBR_Subsurface) != 0) + { + const float subsurfacePower = 12.234; + float forwardScatter = exp2(saturate(-VdotL) * subsurfacePower - subsurfacePower); + float backScatter = saturate(satNdotL * surfaceProperties.Thickness + (1.0 - surfaceProperties.Thickness)) * 0.5; + float subsurface = lerp(backScatter, 1, forwardScatter) * (1.0 - surfaceProperties.Thickness); + transmission += surfaceProperties.SubsurfaceColor * subsurface * lightColor; + } + else if ((PBRFlags & TruePBR_TwoLayer) != 0) + { + float3 coatH = normalize(coatV + coatL); + + float coatNdotL = satNdotL; + float coatNdotV = satNdotV; + float coatNdotH = satNdotH; + float coatVdotH = satVdotH; + [branch] if ((PBRFlags & TruePBR_CoatNormal) != 0) + { + coatNdotL = clamp(dot(coatN, coatL), 1e-5, 1); + coatNdotV = saturate(abs(dot(coatN, coatV)) + 1e-5); + coatNdotH = saturate(dot(coatN, coatH)); + coatVdotH = saturate(dot(coatV, coatH)); + } + + float3 coatF; + float3 coatSpecular = PI * GetSpecularDirectLightMultiplierMicrofacet(surfaceProperties.CoatRoughness, surfaceProperties.CoatF0, coatNdotL, coatNdotV, coatNdotH, coatVdotH, coatF) * coatLightColor * coatNdotL; + + float3 layerAttenuation = 1 - coatF * surfaceProperties.CoatStrength; + diffuse *= layerAttenuation; + specular *= layerAttenuation; + + coatDiffuse += coatLightColor * coatNdotL; + specular += coatSpecular * surfaceProperties.CoatStrength; + } +#endif + } +} + +void GetPBRIndirectLobeWeights(out float3 diffuseLobeWeight, out float3 specularLobeWeight, float3 N, float3 V, float3 diffuseColor, PBRSurfaceProperties surfaceProperties) +{ + diffuseLobeWeight = 0; + specularLobeWeight = 0; + + float NdotV = saturate(dot(N, V)); + +#if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + [branch] if ((PBRFlags & TruePBR_HairMarschner) != 0) + { + float3 L = normalize(V - N * dot(V, N)); + float NdotL = dot(N, L); + float VdotL = dot(V, L); + diffuseLobeWeight = GetHairColorMarschner(N, V, L, NdotL, NdotV, VdotL, 1, 0, 0.2, surfaceProperties); + } + else +#endif + { + diffuseLobeWeight = diffuseColor; + +#if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + [branch] if ((PBRFlags & TruePBR_Subsurface) != 0) + { + diffuseLobeWeight += surfaceProperties.SubsurfaceColor; + } + [branch] if ((PBRFlags & TruePBR_Fuzz) != 0) + { + diffuseLobeWeight += surfaceProperties.FuzzColor * surfaceProperties.FuzzWeight; + } +#endif + + float2 specularBRDF = GetEnvBRDFApproxLazarov(surfaceProperties.Roughness, NdotV); + specularLobeWeight = surfaceProperties.F0 * specularBRDF.x + specularBRDF.y; + + diffuseLobeWeight *= (1 - specularLobeWeight); + + [branch] if (pbrSettings.UseMultipleScattering) + { + specularLobeWeight *= 1 + surfaceProperties.F0 * (1 / (specularBRDF.x + specularBRDF.y) - 1); + } + + [branch] if ((PBRFlags & TruePBR_TwoLayer) != 0) + { + float2 coatSpecularBRDF = GetEnvBRDFApproxLazarov(surfaceProperties.CoatRoughness, NdotV); + float3 coatSpecularLobeWeight = surfaceProperties.CoatF0 * coatSpecularBRDF.x + coatSpecularBRDF.y; + [branch] if (pbrSettings.UseMultipleScattering) + { + coatSpecularLobeWeight *= 1 + surfaceProperties.CoatF0 * (1 / (coatSpecularBRDF.x + coatSpecularBRDF.y) - 1); + } + float3 coatF = GetFresnelFactorSchlick(surfaceProperties.CoatF0, NdotV); + + float3 layerAttenuation = 1 - coatF * surfaceProperties.CoatStrength; + diffuseLobeWeight *= layerAttenuation; + specularLobeWeight *= layerAttenuation; + + [branch] if ((PBRFlags & TruePBR_ColoredCoat) != 0) + { + float3 coatDiffuseLobeWeight = surfaceProperties.CoatColor * (1 - coatSpecularLobeWeight); + diffuseLobeWeight += coatDiffuseLobeWeight * surfaceProperties.CoatStrength; + } + specularLobeWeight += coatSpecularLobeWeight * surfaceProperties.CoatStrength; + } + } + + float3 diffuseAO = surfaceProperties.AO; + float3 specularAO = SpecularAOLagarde(NdotV, surfaceProperties.AO, surfaceProperties.Roughness); + [branch] if (pbrSettings.UseMultiBounceAO) + { + diffuseAO = MultiBounceAO(diffuseColor, diffuseAO.x).y; + specularAO = MultiBounceAO(surfaceProperties.F0, specularAO.x).y; + } + + diffuseLobeWeight *= diffuseAO; + specularLobeWeight *= specularAO; +} \ No newline at end of file diff --git a/package/Shaders/Common/SharedData.hlsli b/package/Shaders/Common/SharedData.hlsli index 9b154d71c..af7c5e844 100644 --- a/package/Shaders/Common/SharedData.hlsli +++ b/package/Shaders/Common/SharedData.hlsli @@ -118,6 +118,15 @@ struct LightLimitFixSettings # include "Skylighting/Skylighting.hlsli" # undef SL_INCL_STRUCT +struct PBRSettings +{ + float DirectionalLightColorMultiplier; + float PointLightColorMultiplier; + float AmbientLightColorMultiplier; + bool UseMultipleScattering; + bool UseMultiBounceAO; +}; + cbuffer FeatureData : register(b6) { GrassLightingSettings grassLightingSettings; @@ -127,6 +136,7 @@ cbuffer FeatureData : register(b6) WetnessEffects wetnessEffects; LightLimitFixSettings lightLimitFixSettings; SkylightingSettings skylightingSettings; + PBRSettings pbrSettings; }; Texture2D TexDepthSampler : register(t20); diff --git a/package/Shaders/DeferredCompositeCS.hlsl b/package/Shaders/DeferredCompositeCS.hlsl index 84659421d..fda36db46 100644 --- a/package/Shaders/DeferredCompositeCS.hlsl +++ b/package/Shaders/DeferredCompositeCS.hlsl @@ -49,10 +49,14 @@ Texture3D SkylightingProbeArray : register(t9); half3 diffuseColor = MainRW[dispatchID.xy]; half3 specularColor = SpecularTexture[dispatchID.xy]; half3 albedo = AlbedoTexture[dispatchID.xy]; - half2 snowParameters = Masks2Texture[dispatchID.xy].xy; + half3 masks2 = Masks2Texture[dispatchID.xy]; + + half2 snowParameters = masks2.xy; + half pbrWeight = masks2.z; half glossiness = normalGlossiness.z; - half3 color = diffuseColor + specularColor; + + half3 color = lerp(diffuseColor + specularColor, Lin2sRGB(sRGB2Lin(diffuseColor) + sRGB2Lin(specularColor)), pbrWeight); #if defined(DYNAMIC_CUBEMAPS) @@ -81,11 +85,14 @@ Texture3D SkylightingProbeArray : register(t9); half roughness = 1.0 - glossiness; half level = roughness * 7.0; + half3 directionalAmbientColor = sRGB2Lin(mul(DirectionalAmbient, half4(R, 1.0))); + half3 finalIrradiance = lerp(0, directionalAmbientColor, pbrWeight); + # if defined(INTERIOR) half3 specularIrradiance = EnvTexture.SampleLevel(LinearSampler, R, level).xyz; specularIrradiance = sRGB2Lin(specularIrradiance); - color += reflectance * specularIrradiance; + finalIrradiance += specularIrradiance; # elif defined(SKYLIGHTING) # if defined(VR) float3 positionMS = positionWS + (eyeIndex == 1 ? CameraPosAdjust[1] - CameraPosAdjust[0] : 0); @@ -113,15 +120,16 @@ Texture3D SkylightingProbeArray : register(t9); specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level).xyz; specularIrradianceReflections = sRGB2Lin(specularIrradianceReflections); } - - color += reflectance * lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); + finalIrradiance += lerp(specularIrradiance, specularIrradianceReflections, skylightingSpecular); # else half3 specularIrradianceReflections = EnvReflectionsTexture.SampleLevel(LinearSampler, R, level).xyz; specularIrradianceReflections = sRGB2Lin(specularIrradianceReflections); - color += reflectance * specularIrradianceReflections; + finalIrradiance += specularIrradianceReflections; # endif + color += reflectance * finalIrradiance; + color = Lin2sRGB(color); } diff --git a/package/Shaders/Lighting.hlsl b/package/Shaders/Lighting.hlsl index 6fde510b4..40debf424 100644 --- a/package/Shaders/Lighting.hlsl +++ b/package/Shaders/Lighting.hlsl @@ -393,7 +393,7 @@ struct PS_OUTPUT float4 Specular : SV_Target4; float4 Reflectance : SV_Target5; float4 Masks : SV_Target6; -# if defined(SNOW) +# if defined(SNOW) || defined(TRUE_PBR) float4 Parameters : SV_Target7; # endif # if defined(TERRAIN_BLENDING) @@ -431,6 +431,12 @@ SamplerState SampColorSampler : register(s0); # define SampLandNormal4Sampler SampColorSampler # define SampLandNormal5Sampler SampColorSampler # define SampLandNormal6Sampler SampColorSampler +# define SampRMAOSSampler SampColorSampler +# define SampLandRMAOS2Sampler SampColorSampler +# define SampLandRMAOS3Sampler SampColorSampler +# define SampLandRMAOS4Sampler SampColorSampler +# define SampLandRMAOS5Sampler SampColorSampler +# define SampLandRMAOS6Sampler SampColorSampler # else @@ -455,6 +461,11 @@ SamplerState SampEnvSampler : register(s4); SamplerState SampEnvMaskSampler : register(s5); # endif +# if defined(TRUE_PBR) +SamplerState SampParallaxSampler : register(s4); +SamplerState SampRMAOSSampler : register(s5); +# endif + SamplerState SampGlowSampler : register(s6); # if defined(MULTI_LAYER_PARALLAX) @@ -502,6 +513,24 @@ Texture2D TexLandNormal4Sampler : register(t10); Texture2D TexLandNormal5Sampler : register(t11); Texture2D TexLandNormal6Sampler : register(t12); +# if defined(TRUE_PBR) + +Texture2D TexLandDisplacement0Sampler : register(t80); +Texture2D TexLandDisplacement1Sampler : register(t81); +Texture2D TexLandDisplacement2Sampler : register(t82); +Texture2D TexLandDisplacement3Sampler : register(t83); +Texture2D TexLandDisplacement4Sampler : register(t84); +Texture2D TexLandDisplacement5Sampler : register(t85); + +Texture2D TexRMAOSSampler : register(t86); +Texture2D TexLandRMAOS2Sampler : register(t87); +Texture2D TexLandRMAOS3Sampler : register(t88); +Texture2D TexLandRMAOS4Sampler : register(t89); +Texture2D TexLandRMAOS5Sampler : register(t90); +Texture2D TexLandRMAOS6Sampler : register(t91); + +# endif + # else Texture2D TexColorSampler : register(t0); @@ -524,6 +553,11 @@ TextureCube TexEnvSampler : register(t4); Texture2D TexEnvMaskSampler : register(t5); # endif +# if defined(TRUE_PBR) +Texture2D TexParallaxSampler : register(t4); +Texture2D TexRMAOSSampler : register(t5); +# endif + Texture2D TexGlowSampler : register(t6); # if defined(MULTI_LAYER_PARALLAX) @@ -574,13 +608,29 @@ cbuffer PerMaterial : register(b1) 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 float4 IBLParams : packoffset(c8); + +# if !defined(TRUE_PBR) float4 LandscapeTexture1to4IsSnow : packoffset(c9); float4 LandscapeTexture5to6IsSnow : packoffset(c10); // bEnableSnowMask in z, inverse iLandscapeMultiNormalTilingFactor in w float4 LandscapeTexture1to4IsSpecPower : packoffset(c11); float4 LandscapeTexture5to6IsSpecPower : packoffset(c12); float4 SnowRimLightParameters : packoffset(c13); // fSnowRimLightIntensity in x, fSnowGeometrySpecPower in y, fSnowNormalSpecPower in z, bEnableSnowRimLighting in w +# endif + +# if defined(TRUE_PBR) && defined(LANDSCAPE) + float3 LandscapeTexture2PBRParams : packoffset(c9); + float3 LandscapeTexture3PBRParams : packoffset(c10); + float3 LandscapeTexture4PBRParams : packoffset(c11); + float3 LandscapeTexture5PBRParams : packoffset(c12); + float3 LandscapeTexture6PBRParams : packoffset(c13); +# endif + float4 CharacterLightParams : packoffset(c14); // VR is [9] instead of [15] + + uint PBRFlags : packoffset(c15.x); + float3 PBRParams1 : packoffset(c15.y); // roughness scale, displacement scale, specular level + float4 PBRParams2 : packoffset(c16); // subsurface color, subsurface opacity }; cbuffer PerGeometry : register(b2) @@ -716,13 +766,17 @@ float GetLodLandBlendMultiplier(float parameter, float mask) float GetLandSnowMaskValue(float alpha) { +# if !defined(TRUE_PBR) return alpha * LandscapeTexture5to6IsSnow.z + (1 + -LandscapeTexture5to6IsSnow.z); +# else + return 0; +# endif } float3 GetLandNormal(float landSnowMask, float3 normal, float2 uv, SamplerState sampNormal, Texture2D texNormal) { float3 landNormal = TransformNormal(normal); -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) if (landSnowMask > 1e-5 && LandscapeTexture5to6IsSnow.w != 1.0) { float3 snowNormal = float3(-1, -1, 1) * @@ -739,7 +793,7 @@ float3 GetLandNormal(float landSnowMask, float3 normal, float2 uv, SamplerState # endif } -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) float3 GetSnowSpecularColor(PS_INPUT input, float3 modelNormal, float3 viewDirection) { if (SnowRimLightParameters.w > 1e-5) { @@ -860,6 +914,12 @@ float GetSnowParameterY(float texProjTmp, float alpha) # endif } +float3 ApplyFogAndClampColor(float3 srcColor, float4 fogParam, float3 clampColor, out float3 preClampColor) +{ + preClampColor = (srcColor - lerp(srcColor, fogParam.xyz, fogParam.w) * FogColor.w) * FrameParams.y; + return min(preClampColor + clampColor, srcColor); +} + # if defined(WATER_CAUSTICS) # include "WaterCaustics/WaterCaustics.hlsli" # endif @@ -877,10 +937,18 @@ float GetSnowParameterY(float texProjTmp, float alpha) # undef WETNESS_EFFECTS # endif -# if defined(EXTENDED_MATERIALS) && !defined(LOD) && (defined(PARALLAX) || defined(LANDSCAPE) || defined(ENVMAP)) +# if defined(EXTENDED_MATERIALS) && !defined(LOD) && (defined(PARALLAX) || defined(LANDSCAPE) || defined(ENVMAP) || defined(TRUE_PBR)) # define EMAT # endif +# if defined(DYNAMIC_CUBEMAPS) +# include "DynamicCubemaps/DynamicCubemaps.hlsli" +# endif + +# if defined(TRUE_PBR) +# include "Common/PBR.hlsli" +# endif + # if defined(EMAT) # include "ExtendedMaterials/ExtendedMaterials.hlsli" # endif @@ -897,10 +965,6 @@ float GetSnowParameterY(float texProjTmp, float alpha) # include "LightLimitFix/LightLimitFix.hlsli" # endif -# if defined(DYNAMIC_CUBEMAPS) -# include "DynamicCubemaps/DynamicCubemaps.hlsli" -# endif - # if defined(TREE_ANIM) # undef WETNESS_EFFECTS # endif @@ -951,7 +1015,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif // defined (SKINNED) || !defined (MODELSPACENORMALS) -# if defined(LANDSCAPE) +# if defined(LANDSCAPE) && !defined(TRUE_PBR) float shininess = dot(input.LandBlendWeights1, LandscapeTexture1to4IsSpecPower) + input.LandBlendWeights2.x * LandscapeTexture5to6IsSpecPower.x + input.LandBlendWeights2.y * LandscapeTexture5to6IsSpecPower.y; # else float shininess = SpecularColor.w; @@ -992,17 +1056,37 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float mipLevels[6]; float sh0; float pixelOffset; + +# if defined(EMAT) + DisplacementParams displacementParams[6]; + displacementParams[0].DisplacementScale = 1.f; + displacementParams[0].DisplacementOffset = 0.f; + displacementParams[0].HeightScale = 1.f; +# endif # else float mipLevel; float sh0; float pixelOffset; + +# if defined(EMAT) + DisplacementParams displacementParams; + displacementParams.DisplacementScale = 1.f; + displacementParams.DisplacementOffset = 0.f; + displacementParams.HeightScale = 1.f; +# endif # endif // LANDSCAPE + float3 entryNormal = 0; + float3 entryNormalTS = 0; + float eta = 1; + float3 refractedViewDirection = viewDirection; + float3 refractedViewDirectionWS = worldSpaceViewDirection; + # if defined(EMAT) # if defined(PARALLAX) if (extendedMaterialSettings.EnableParallax) { mipLevel = GetMipLevel(uv, TexParallaxSampler); - uv = GetParallaxCoords(viewPosition.z, uv, mipLevel, viewDirection, tbnTr, screenNoise, TexParallaxSampler, SampParallaxSampler, 0, pixelOffset); + uv = GetParallaxCoords(viewPosition.z, uv, mipLevel, viewDirection, tbnTr, screenNoise, TexParallaxSampler, SampParallaxSampler, 0, displacementParams, pixelOffset); if (extendedMaterialSettings.EnableShadows && parallaxShadowQuality > 0.0f) sh0 = TexParallaxSampler.SampleLevel(SampParallaxSampler, uv, mipLevel).x; } @@ -1021,7 +1105,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace if (envMaskTest > (4.0 / 255.0)) { complexMaterialParallax = true; mipLevel = GetMipLevel(uv, TexEnvMaskSampler); - uv = GetParallaxCoords(viewPosition.z, uv, mipLevel, viewDirection, tbnTr, screenNoise, TexEnvMaskSampler, SampTerrainParallaxSampler, 3, pixelOffset); + uv = GetParallaxCoords(viewPosition.z, uv, mipLevel, viewDirection, tbnTr, screenNoise, TexEnvMaskSampler, SampTerrainParallaxSampler, 3, displacementParams, pixelOffset); if (extendedMaterialSettings.EnableShadows && parallaxShadowQuality > 0.0f) sh0 = TexEnvMaskSampler.SampleLevel(SampEnvMaskSampler, uv, mipLevel).w; } @@ -1031,7 +1115,38 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace } # endif // ENVMAP -# endif // EMAT + +# if defined(TRUE_PBR) && !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + bool PBRParallax = false; + [branch] if ((PBRFlags & TruePBR_HasDisplacement) != 0) + { + PBRParallax = true; + displacementParams.HeightScale = PBRParams1.y; + [branch] if ((PBRFlags & TruePBR_InterlayerParallax) != 0) + { + displacementParams.DisplacementScale = 0.5; + displacementParams.DisplacementOffset = -0.25; + eta = (1 - sqrt(MultiLayerParallaxData.y)) / (1 + sqrt(MultiLayerParallaxData.y)); + [branch] if ((PBRFlags & TruePBR_CoatNormal) != 0) + { + entryNormalTS = normalize(TransformNormal(TexBackLightSampler.Sample(SampBackLightSampler, uvOriginal))); + } + else + { + entryNormalTS = normalize(TransformNormal(TexNormalSampler.Sample(SampNormalSampler, uvOriginal))); + } + entryNormal = normalize(mul(tbn, entryNormalTS)); + refractedViewDirection = -refract(-viewDirection, entryNormal, eta); + refractedViewDirectionWS = normalize(mul(input.World[eyeIndex], float4(refractedViewDirection, 0))); + } + mipLevel = GetMipLevel(uv, TexParallaxSampler); + uv = GetParallaxCoords(viewPosition.z, uv, mipLevel, refractedViewDirection, tbnTr, screenNoise, TexParallaxSampler, SampParallaxSampler, 0, displacementParams, pixelOffset); + if (extendedMaterialSettings.EnableShadows && parallaxShadowQuality > 0.0f) + sh0 = TexParallaxSampler.SampleLevel(SampParallaxSampler, uv, mipLevel).x; + } +# endif // TRUE_PBR + +# endif // EMAT # if defined(SNOW) bool useSnowSpecular = true; @@ -1073,8 +1188,23 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace mipLevels[3] = GetMipLevel(uv, TexLandColor4Sampler); mipLevels[4] = GetMipLevel(uv, TexLandColor5Sampler); mipLevels[5] = GetMipLevel(uv, TexLandColor6Sampler); + + displacementParams[1] = displacementParams[0]; + displacementParams[2] = displacementParams[0]; + displacementParams[3] = displacementParams[0]; + displacementParams[4] = displacementParams[0]; + displacementParams[5] = displacementParams[0]; +# if defined(TRUE_PBR) + displacementParams[0].HeightScale = PBRParams1.y; + displacementParams[1].HeightScale = LandscapeTexture2PBRParams.y; + displacementParams[2].HeightScale = LandscapeTexture3PBRParams.y; + displacementParams[3].HeightScale = LandscapeTexture4PBRParams.y; + displacementParams[4].HeightScale = LandscapeTexture5PBRParams.y; + displacementParams[5].HeightScale = LandscapeTexture6PBRParams.y; +# endif + float weights[6]; - uv = GetParallaxCoords(input, viewPosition.z, uv, mipLevels, viewDirection, tbnTr, screenNoise, pixelOffset, weights); + uv = GetParallaxCoords(input, viewPosition.z, uv, mipLevels, viewDirection, tbnTr, screenNoise, displacementParams, pixelOffset, weights); if (extendedMaterialSettings.EnableComplexMaterial) { input.LandBlendWeights1.x = weights[0]; input.LandBlendWeights1.y = weights[1]; @@ -1084,7 +1214,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace input.LandBlendWeights2.y = weights[5]; } if (extendedMaterialSettings.EnableShadows && parallaxShadowQuality > 0.0f) { - sh0 = GetTerrainHeight(input, uv, mipLevels, parallaxShadowQuality, weights); + sh0 = GetTerrainHeight(input, uv, mipLevels, displacementParams, parallaxShadowQuality, weights); } } # endif // EMAT @@ -1104,12 +1234,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float4 normal = 0; float4 glossiness = 0; + float4 rawRMAOS = 0; + # if defined(LANDSCAPE) if (input.LandBlendWeights1.x > 0.0) { # endif // LANDSCAPE float4 rawBaseColor = TexColorSampler.Sample(SampColorSampler, diffuseUv); - baseColor = rawBaseColor; float landSnowMask1 = GetLandSnowMaskValue(baseColor.w); @@ -1147,12 +1278,28 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace normal.xyz = GetWorldMapNormal(input, normal.xyz, rawBaseColor.xyz); # endif // WORLD_MAP +# if defined(TRUE_PBR) +# if defined(LODLANDNOISE) + rawRMAOS = float4(1, 0, 1, 0.04); +# elif defined(LANDSCAPE) + [branch] if ((PBRFlags & TruePBR_LandTile0PBR) != 0) + { + rawRMAOS = input.LandBlendWeights1.x * TexRMAOSSampler.Sample(SampRMAOSSampler, diffuseUv) * float4(PBRParams1.x, 1, 1, PBRParams1.z); + } + else + { + rawRMAOS = input.LandBlendWeights1.x * float4(1 - glossiness.x, 0, 1, 0.04); + } +# else + rawRMAOS = TexRMAOSSampler.Sample(SampRMAOSSampler, diffuseUv) * float4(PBRParams1.x, 1, 1, PBRParams1.z); +# endif +# endif + # if defined(LANDSCAPE) baseColor *= input.LandBlendWeights1.x; normal *= input.LandBlendWeights1.x; glossiness *= input.LandBlendWeights1.x; } - # endif // LANDSCAPE # if defined(EMAT) && defined(ENVMAP) @@ -1170,7 +1317,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # if defined(LANDSCAPE) -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) float landSnowMask = LandscapeTexture1to4IsSnow.x * input.LandBlendWeights1.x; # endif // SNOW @@ -1182,9 +1329,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace landNormal2.xyz = GetLandNormal(landSnowMask2, landNormal2.xyz, uv, SampLandNormal2Sampler, TexLandNormal2Sampler); normal.xyz += input.LandBlendWeights1.yyy * landNormal2.xyz; glossiness += input.LandBlendWeights1.y * landNormal2.w; -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) landSnowMask += LandscapeTexture1to4IsSnow.y * input.LandBlendWeights1.y * landSnowMask2; # endif // SNOW + +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_LandTile1PBR) != 0) + { + rawRMAOS += input.LandBlendWeights1.y * TexLandRMAOS2Sampler.Sample(SampLandRMAOS2Sampler, uv) * float4(LandscapeTexture2PBRParams.x, 1, 1, LandscapeTexture2PBRParams.z); + } + else + { + rawRMAOS += input.LandBlendWeights1.y * float4(1 - landNormal2.w, 0, 1, 1); + } +# endif } if (input.LandBlendWeights1.z > 0.0) { @@ -1195,9 +1353,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace landNormal3.xyz = GetLandNormal(landSnowMask3, landNormal3.xyz, uv, SampLandNormal3Sampler, TexLandNormal3Sampler); normal.xyz += input.LandBlendWeights1.zzz * landNormal3.xyz; glossiness += input.LandBlendWeights1.z * landNormal3.w; -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) landSnowMask += LandscapeTexture1to4IsSnow.z * input.LandBlendWeights1.z * landSnowMask3; # endif // SNOW + +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_LandTile2PBR) != 0) + { + rawRMAOS += input.LandBlendWeights1.z * TexLandRMAOS3Sampler.Sample(SampLandRMAOS3Sampler, uv) * float4(LandscapeTexture3PBRParams.x, 1, 1, LandscapeTexture3PBRParams.z); + } + else + { + rawRMAOS += input.LandBlendWeights1.z * float4(1 - landNormal3.w, 0, 1, 1); + } +# endif } if (input.LandBlendWeights1.w > 0.0) { @@ -1208,9 +1377,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace landNormal4.xyz = GetLandNormal(landSnowMask4, landNormal4.xyz, uv, SampLandNormal4Sampler, TexLandNormal4Sampler); normal.xyz += input.LandBlendWeights1.www * landNormal4.xyz; glossiness += input.LandBlendWeights1.w * landNormal4.w; -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) landSnowMask += LandscapeTexture1to4IsSnow.w * input.LandBlendWeights1.w * landSnowMask4; # endif // SNOW + +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_LandTile3PBR) != 0) + { + rawRMAOS += input.LandBlendWeights1.w * TexLandRMAOS4Sampler.Sample(SampLandRMAOS4Sampler, uv) * float4(LandscapeTexture4PBRParams.x, 1, 1, LandscapeTexture4PBRParams.z); + } + else + { + rawRMAOS += input.LandBlendWeights1.w * float4(1 - landNormal4.w, 0, 1, 1); + } +# endif } if (input.LandBlendWeights2.x > 0.0) { @@ -1221,9 +1401,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace landNormal5.xyz = GetLandNormal(landSnowMask5, landNormal5.xyz, uv, SampLandNormal5Sampler, TexLandNormal5Sampler); normal.xyz += input.LandBlendWeights2.xxx * landNormal5.xyz; glossiness += input.LandBlendWeights2.x * landNormal5.w; -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) landSnowMask += LandscapeTexture5to6IsSnow.x * input.LandBlendWeights2.x * landSnowMask5; # endif // SNOW + +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_LandTile4PBR) != 0) + { + rawRMAOS += input.LandBlendWeights2.x * TexLandRMAOS5Sampler.Sample(SampLandRMAOS5Sampler, uv) * float4(LandscapeTexture5PBRParams.x, 1, 1, LandscapeTexture5PBRParams.z); + } + else + { + rawRMAOS += input.LandBlendWeights2.x * float4(1 - landNormal5.w, 0, 1, 1); + } +# endif } if (input.LandBlendWeights2.y > 0.0) { @@ -1234,24 +1425,39 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace landNormal6.xyz = GetLandNormal(landSnowMask6, landNormal6.xyz, uv, SampLandNormal6Sampler, TexLandNormal6Sampler); normal.xyz += input.LandBlendWeights2.yyy * landNormal6.xyz; glossiness += input.LandBlendWeights2.y * landNormal6.w; -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) landSnowMask += LandscapeTexture5to6IsSnow.y * input.LandBlendWeights2.y * landSnowMask6; # endif // SNOW + +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_LandTile5PBR) != 0) + { + rawRMAOS += input.LandBlendWeights2.y * TexLandRMAOS6Sampler.Sample(SampLandRMAOS6Sampler, uv) * float4(LandscapeTexture6PBRParams.x, 1, 1, LandscapeTexture6PBRParams.z); + } + else + { + rawRMAOS += input.LandBlendWeights2.y * float4(1 - landNormal6.w, 0, 1, 1); + } +# endif } # if defined(LOD_LAND_BLEND) - float4 lodBlendColor = TexLandLodBlend1Sampler.Sample(SampLandLodBlend1Sampler, input.TexCoord0.zw); - float lodBlendTmp = GetLodLandBlendParameter(lodBlendColor.xyz); + float4 lodLandColor = TexLandLodBlend1Sampler.Sample(SampLandLodBlend1Sampler, input.TexCoord0.zw); + float lodBlendParameter = GetLodLandBlendParameter(lodLandColor.xyz); float lodBlendMask = TexLandLodBlend2Sampler.Sample(SampLandLodBlend2Sampler, 3.0.xx * input.TexCoord0.zw).x; - float lodBlendMul1 = GetLodLandBlendMultiplier(lodBlendTmp, lodBlendMask); - float lodBlendMul2 = LODTexParams.z * input.LandBlendWeights2.w; + float lodLandFadeFactor = GetLodLandBlendMultiplier(lodBlendParameter, lodBlendMask); + float lodLandBlendFactor = LODTexParams.z * input.LandBlendWeights2.w; + + normal.xyz = lerp(normal.xyz, float3(0, 0, 1), lodLandBlendFactor); + +# if !defined(TRUE_PBR) baseColor.w = 0; - baseColor = lodBlendMul2.xxxx * (lodBlendColor * lodBlendMul1.xxxx - baseColor) + baseColor; - normal.xyz = lodBlendMul2.xxx * (float3(0, 0, 1) - normal.xyz) + normal.xyz; - glossiness += lodBlendMul2 * -glossiness; + baseColor = lerp(baseColor, lodLandColor * lodLandFadeFactor, lodLandBlendFactor); + glossiness = lerp(glossiness, 0, lodLandBlendFactor); +# endif # endif // LOD_LAND_BLEND -# if defined(SNOW) +# if defined(SNOW) && !defined(TRUE_PBR) useSnowSpecular = landSnowMask != 0.0; # endif // SNOW # endif // LANDSCAPE @@ -1267,6 +1473,12 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float numLights = min(7, NumLightNumShadowLight.x); float numShadowLights = min(4, NumLightNumShadowLight.y); +# if defined(TRUE_PBR) && !defined(MODELSPACENORMALS) + if (!frontFace) { + normal.xyz *= -1; + } +# endif + # if defined(MODELSPACENORMALS) && !defined(SKINNED) float4 modelNormal = normal; # else @@ -1285,7 +1497,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace shadowColor = TexShadowMaskSampler.Sample(SampShadowMaskSampler, shadowUV); } - float texProjTmp = 0; + float projWeight = 0; # if defined(PROJECTED_UV) float2 projNoiseUv = ProjectedUVParams.zz * input.TexCoord0.zw; @@ -1296,44 +1508,44 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # else float vertexAlpha = input.Color.w; # endif // defined (TREE_ANIM) || defined (LODOBJECTSHD) - texProjTmp = -ProjectedUVParams.x * projNoise + (dot(modelNormal.xyz, texProj) * vertexAlpha - ProjectedUVParams.w); + projWeight = -ProjectedUVParams.x * projNoise + (dot(modelNormal.xyz, texProj) * vertexAlpha - ProjectedUVParams.w); # if defined(LODOBJECTSHD) - texProjTmp += (-0.5 + input.Color.w) * 2.5; + projWeight += (-0.5 + input.Color.w) * 2.5; # endif // LODOBJECTSHD # if defined(SPARKLE) - if (texProjTmp < 0) + if (projWeight < 0) discard; modelNormal.xyz = projectedNormal; # if defined(SNOW) psout.Parameters.y = 1; # endif // SNOW -# else +# elif !defined(FACEGEN) && !defined(PARALLAX) && !defined(SPARKLE) if (ProjectedUVParams3.w > 0.5) { - float2 projNormalUv = ProjectedUVParams3.x * projNoiseUv; - float3 projNormal = TransformNormal(TexProjNormalSampler.Sample(SampProjNormalSampler, projNormalUv).xyz); - float2 projDetailUv = ProjectedUVParams3.y * projNoiseUv; - float3 projDetail = TexProjDetail.Sample(SampProjDetailSampler, projDetailUv).xyz; - float3 texProjTmp3 = (projDetail * 2.0.xxx + float3(projNormal.xy, -1)); - texProjTmp3.xy += -1.0.xx; - texProjTmp3.z = projNormal.z * texProjTmp3.z; - float3 finalProjNormal = normalize(texProjTmp3); - float3 projDiffuse = TexProjDiffuseSampler.Sample(SampProjDiffuseSampler, projNormalUv).xyz; - float texProjTmp1 = saturate(5 * (0.1 + texProjTmp)); - float texProjTmp2 = (texProjTmp1 * -2 + 3) * (texProjTmp1 * texProjTmp1); - normal.xyz = texProjTmp2.xxx * (finalProjNormal - normal.xyz) + normal.xyz; - baseColor.xyz = texProjTmp2.xxx * (projDiffuse * ProjectedUVParams2.xyz - baseColor.xyz) + baseColor.xyz; + float2 projNormalDiffuseUv = ProjectedUVParams3.x * projNoiseUv; + float3 projNormal = TransformNormal(TexProjNormalSampler.Sample(SampProjNormalSampler, projNormalDiffuseUv).xyz); + float2 projDetailNormalUv = ProjectedUVParams3.y * projNoiseUv; + float3 projDetailNormal = TexProjDetail.Sample(SampProjDetailSampler, projDetailNormalUv).xyz; + float3 finalProjNormal = normalize(TransformNormal(projDetailNormal) * float3(1, 1, projNormal.z) + float3(projNormal.xy, 0)); + float3 projBaseColor = TexProjDiffuseSampler.Sample(SampProjDiffuseSampler, projNormalDiffuseUv).xyz * ProjectedUVParams2.xyz; + float projBlendWeight = smoothstep(0, 1, 5 * (0.1 + projWeight)); +# if defined(TRUE_PBR) + projBaseColor = saturate(EnvmapData.xyz * projBaseColor); + rawRMAOS.xyw = lerp(rawRMAOS.xyw, float3(ParallaxOccData.x, 0, ParallaxOccData.y), projBlendWeight); +# endif + normal.xyz = lerp(normal.xyz, finalProjNormal, projBlendWeight); + baseColor.xyz = lerp(baseColor.xyz, projBaseColor, projBlendWeight); # if defined(SNOW) useSnowDecalSpecular = true; - psout.Parameters.y = GetSnowParameterY(texProjTmp2, baseColor.w); + psout.Parameters.y = GetSnowParameterY(projBlendWeight, baseColor.w); # endif // SNOW } else { - if (texProjTmp > 0) { + if (projWeight > 0) { baseColor.xyz = ProjectedUVParams2.xyz; # if defined(SNOW) useSnowDecalSpecular = true; - psout.Parameters.y = GetSnowParameterY(texProjTmp, baseColor.w); + psout.Parameters.y = GetSnowParameterY(projWeight, baseColor.w); # endif // SNOW } else { # if defined(SNOW) @@ -1347,7 +1559,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif // SPECULAR # endif // SPARKLE -# elif defined(SNOW) +# elif defined(SNOW) && !defined(TRUE_PBR) # if defined(LANDSCAPE) psout.Parameters.y = landSnowMask; # else @@ -1356,7 +1568,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # if defined(WORLD_MAP) - baseColor.xyz = GetWorldMapBaseColor(rawBaseColor.xyz, baseColor.xyz, texProjTmp); + baseColor.xyz = GetWorldMapBaseColor(rawBaseColor.xyz, baseColor.xyz, projWeight); # endif // WORLD_MAP float3 worldSpaceNormal = modelNormal; @@ -1368,6 +1580,100 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 screenSpaceNormal = normalize(WorldToView(worldSpaceNormal, false, eyeIndex)); +# if defined(TRUE_PBR) + PBRSurfaceProperties pbrSurfaceProperties; + + pbrSurfaceProperties.Roughness = saturate(rawRMAOS.x); + pbrSurfaceProperties.Metallic = saturate(rawRMAOS.y); + pbrSurfaceProperties.AO = rawRMAOS.z; + pbrSurfaceProperties.F0 = lerp(saturate(rawRMAOS.w), baseColor.xyz, pbrSurfaceProperties.Metallic); + + pbrSurfaceProperties.SubsurfaceColor = 0; + pbrSurfaceProperties.Thickness = 0; + + pbrSurfaceProperties.CoatColor = 0; + pbrSurfaceProperties.CoatStrength = 0; + pbrSurfaceProperties.CoatRoughness = 0; + pbrSurfaceProperties.CoatF0 = 0.04; + + pbrSurfaceProperties.FuzzColor = 0; + pbrSurfaceProperties.FuzzWeight = 0; + + baseColor.xyz *= 1 - pbrSurfaceProperties.Metallic; + + pbrSurfaceProperties.BaseColor = baseColor.xyz; + + float3 coatModelNormal = modelNormal.xyz; + float3 coatWorldNormal = worldSpaceNormal; + +# if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + [branch] if ((PBRFlags & TruePBR_Subsurface) != 0) + { + pbrSurfaceProperties.SubsurfaceColor = PBRParams2.xyz; + pbrSurfaceProperties.Thickness = PBRParams2.w; + [branch] if ((PBRFlags & TruePBR_HasFeatureTexture0) != 0) + { + float4 sampledSubsurfaceProperties = TexRimSoftLightWorldMapOverlaySampler.Sample(SampRimSoftLightWorldMapOverlaySampler, uv); + pbrSurfaceProperties.SubsurfaceColor *= sampledSubsurfaceProperties.xyz; + pbrSurfaceProperties.Thickness *= sampledSubsurfaceProperties.w; + } + } + else if ((PBRFlags & TruePBR_TwoLayer) != 0) + { + pbrSurfaceProperties.CoatColor = PBRParams2.xyz; + pbrSurfaceProperties.CoatStrength = PBRParams2.w; + pbrSurfaceProperties.CoatRoughness = MultiLayerParallaxData.x; + pbrSurfaceProperties.CoatF0 = MultiLayerParallaxData.y; + + float2 coatUv = uv; + [branch] if ((PBRFlags & TruePBR_InterlayerParallax) != 0) + { + coatUv = uvOriginal; + } + [branch] if ((PBRFlags & TruePBR_HasFeatureTexture0) != 0) + { + float4 sampledCoatProperties = TexRimSoftLightWorldMapOverlaySampler.Sample(SampRimSoftLightWorldMapOverlaySampler, coatUv); + pbrSurfaceProperties.CoatColor *= sampledCoatProperties.xyz; + pbrSurfaceProperties.CoatStrength *= sampledCoatProperties.w; + } + [branch] if ((PBRFlags & TruePBR_HasFeatureTexture1) != 0) + { + float4 sampledCoatProperties = TexBackLightSampler.Sample(SampBackLightSampler, coatUv); + pbrSurfaceProperties.CoatRoughness *= sampledCoatProperties.w; + [branch] if ((PBRFlags & TruePBR_CoatNormal) != 0) + { + coatModelNormal = normalize(mul(tbn, TransformNormal(sampledCoatProperties.xyz))); + } + +# if !defined(DRAW_IN_WORLDSPACE) + [flatten] if (!input.WorldSpace) + { + coatWorldNormal = normalize(mul(input.World[eyeIndex], float4(coatModelNormal, 0))); + } +# endif + } + } + + [branch] if ((PBRFlags & TruePBR_Fuzz) != 0) + { + pbrSurfaceProperties.FuzzColor = MultiLayerParallaxData.xyz; + pbrSurfaceProperties.FuzzWeight = MultiLayerParallaxData.w; + [branch] if ((PBRFlags & TruePBR_HasFeatureTexture1) != 0) + { + float4 sampledFuzzProperties = TexBackLightSampler.Sample(SampBackLightSampler, uv); + pbrSurfaceProperties.FuzzColor *= sampledFuzzProperties.xyz; + pbrSurfaceProperties.FuzzWeight *= sampledFuzzProperties.w; + } + } +# endif + + float3 specularColorPBR = 0; + float3 transmissionColor = 0; + + float pbrWeight = 1; + float pbrGlossiness = 1 - pbrSurfaceProperties.Roughness; +# endif // TRUE_PBR + # if !defined(MODELSPACENORMALS) float3 vertexNormal = tbnTr[2]; float3 worldSpaceVertexNormal = vertexNormal; @@ -1384,9 +1690,10 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float waterHeight = waterData.w; float3 dirLightColor = DirLightColor.xyz; + float3 dirLightColorMultiplier = 1; # if defined(WATER_CAUSTICS) - dirLightColor *= ComputeWaterCaustics(waterData, input.WorldPosition.xyz, worldSpaceNormal); + dirLightColorMultiplier *= ComputeWaterCaustics(waterData, input.WorldPosition.xyz, worldSpaceNormal); # endif float selfShadowFactor = 1.0f; @@ -1401,7 +1708,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float dirLightAngle = dot(modelNormal.xyz, DirLightDirection.xyz); if ((PixelShaderDescriptor & _DefShadow) && (PixelShaderDescriptor & _ShadowDir)) { - dirLightColor *= shadowColor.x; + dirLightColorMultiplier *= shadowColor.x; } # if defined(SOFT_LIGHTING) || defined(BACK_LIGHTING) || defined(RIM_LIGHTING) @@ -1410,8 +1717,17 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace bool inDirShadow = ((PixelShaderDescriptor & _DefShadow) && (PixelShaderDescriptor & _ShadowDir) && shadowColor.x == 0) && dirLightAngle > 0.0; # endif + float3 refractedDirLightDirection = DirLightDirection; +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_InterlayerParallax) != 0) + { + refractedDirLightDirection = -refract(-DirLightDirection, coatModelNormal, eta); + } +# endif + float dirDetailShadow = 1.0; float dirShadow = 1.0; + float parallaxShadow = 1; # if defined(SOFT_LIGHTING) || defined(BACK_LIGHTING) || defined(RIM_LIGHTING) if (!inDirShadow && dirLightAngle > 0.0) { # else @@ -1422,17 +1738,21 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # if defined(EMAT) && (defined(SKINNED) || !defined(MODELSPACENORMALS)) - if (!inDirShadow) { - float3 dirLightDirectionTS = mul(DirLightDirection, tbn).xyz; + [branch] if (!inDirShadow) + { + float3 dirLightDirectionTS = mul(refractedDirLightDirection, tbn).xyz; # if defined(LANDSCAPE) - if (extendedMaterialSettings.EnableTerrainParallax && extendedMaterialSettings.EnableShadows) - dirDetailShadow *= GetParallaxSoftShadowMultiplierTerrain(input, uv, mipLevels, dirLightDirectionTS, sh0, parallaxShadowQuality, screenNoise); + [branch] if (extendedMaterialSettings.EnableTerrainParallax && extendedMaterialSettings.EnableShadows) + parallaxShadow = GetParallaxSoftShadowMultiplierTerrain(input, uv, mipLevels, dirLightDirectionTS, sh0, parallaxShadowQuality, screenNoise, displacementParams); # elif defined(PARALLAX) - if (extendedMaterialSettings.EnableParallax && extendedMaterialSettings.EnableShadows) - dirDetailShadow *= GetParallaxSoftShadowMultiplier(uv, mipLevel, dirLightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality, screenNoise); + [branch] if (extendedMaterialSettings.EnableParallax && extendedMaterialSettings.EnableShadows) + parallaxShadow = GetParallaxSoftShadowMultiplier(uv, mipLevel, dirLightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality, screenNoise, displacementParams); # elif defined(ENVMAP) - if (complexMaterialParallax && extendedMaterialSettings.EnableShadows) - dirDetailShadow *= GetParallaxSoftShadowMultiplier(uv, mipLevel, dirLightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, parallaxShadowQuality, screenNoise); + [branch] if (complexMaterialParallax && extendedMaterialSettings.EnableShadows) + parallaxShadow = GetParallaxSoftShadowMultiplier(uv, mipLevel, dirLightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, parallaxShadowQuality, screenNoise, displacementParams); +# elif defined(TRUE_PBR) && !defined(LODLANDSCAPE) + [branch] if (PBRParallax && extendedMaterialSettings.EnableShadows) + parallaxShadow = GetParallaxSoftShadowMultiplier(uv, mipLevel, dirLightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality, screenNoise, displacementParams); # endif // LANDSCAPE } # endif // defined(EMAT) && (defined (SKINNED) || !defined \ @@ -1453,43 +1773,68 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace } # endif - dirLightColor *= dirShadow; + dirLightColorMultiplier *= dirShadow; + + float3 noParallaxShadowDirLightColorMultiplier = dirLightColorMultiplier * dirDetailShadow; + dirDetailShadow *= parallaxShadow; + float3 fullShadowDirLightColorMultiplier = dirLightColorMultiplier * dirDetailShadow; float3 diffuseColor = 0.0.xxx; float3 specularColor = 0.0.xxx; float3 lightsDiffuseColor = 0.0.xxx; + float3 coatLightsDiffuseColor = 0.0.xxx; float3 lightsSpecularColor = 0.0.xxx; + float3 lodLandDiffuseColor = 0; + + dirLightColor *= dirLightColorMultiplier; + +# if defined(TRUE_PBR) + { + float3 pbrDirLightColor = AdjustDirectionalLightColorForPBR(DirLightColor.xyz); + + float3 dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor; + GetDirectLightInputPBR(dirDiffuseColor, coatDirDiffuseColor, dirTransmissionColor, dirSpecularColor, modelNormal.xyz, coatModelNormal, refractedViewDirection, viewDirection, refractedDirLightDirection, DirLightDirection, fullShadowDirLightColorMultiplier * pbrDirLightColor, noParallaxShadowDirLightColorMultiplier * pbrDirLightColor, pbrSurfaceProperties); + lightsDiffuseColor += dirDiffuseColor; + coatLightsDiffuseColor += coatDirDiffuseColor; + transmissionColor += dirTransmissionColor; + specularColorPBR += dirSpecularColor; +# if defined(LOD_LAND_BLEND) + lodLandDiffuseColor += dirLightColor * saturate(dirLightAngle) * dirDetailShadow; +# endif + } +# else float3 dirDiffuseColor = dirLightColor * saturate(dirLightAngle) * dirDetailShadow; -# if defined(SOFT_LIGHTING) || defined(RIM_LIGHTING) || defined(BACK_LIGHTING) +# if defined(SOFT_LIGHTING) || defined(RIM_LIGHTING) || defined(BACK_LIGHTING) float backlighting = 1.0 + saturate(dot(viewDirection, -DirLightDirection.xyz)); -# endif +# endif -# if defined(SOFT_LIGHTING) +# if defined(SOFT_LIGHTING) lightsDiffuseColor += dirLightColor * GetSoftLightMultiplier(dirLightAngle) * rimSoftLightColor.xyz * backlighting; -# endif +# endif -# if defined(RIM_LIGHTING) +# if defined(RIM_LIGHTING) lightsDiffuseColor += dirLightColor * GetRimLightMultiplier(DirLightDirection, viewDirection, modelNormal.xyz) * rimSoftLightColor.xyz * backlighting; -# endif +# endif -# if defined(BACK_LIGHTING) +# if defined(BACK_LIGHTING) lightsDiffuseColor += dirLightColor * saturate(-dirLightAngle) * backLightColor.xyz * backlighting; -# endif +# endif if (useSnowSpecular && useSnowDecalSpecular) { -# if defined(SNOW) - lightsSpecularColor = GetSnowSpecularColor(input, modelNormal.xyz, viewDirection); -# endif +# if defined(SNOW) + lightsSpecularColor += GetSnowSpecularColor(input, modelNormal.xyz, viewDirection); +# endif } else { -# if defined(SPECULAR) || defined(SPARKLE) +# if defined(SPECULAR) || defined(SPARKLE) lightsSpecularColor = GetLightSpecularInput(input, DirLightDirection, viewDirection, modelNormal.xyz, dirLightColor.xyz * dirDetailShadow, shininess, uv); -# endif +# endif } lightsDiffuseColor += dirDiffuseColor; +# endif float porosity = 1.0; @@ -1606,35 +1951,54 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float intensityMultiplier = 1 - intensityFactor * intensityFactor; float3 lightColor = PointLightColor[lightIndex].xyz * intensityMultiplier; - + float lightShadow = 1.f; if (PixelShaderDescriptor & _DefShadow) { if (lightIndex < numShadowLights) { - lightColor *= shadowColor[ShadowLightMaskSelect[lightIndex]]; + lightShadow *= shadowColor[ShadowLightMaskSelect[lightIndex]]; } } float3 normalizedLightDirection = normalize(lightDirection); + lightColor *= lightShadow; + +# if defined(TRUE_PBR) + { + float3 pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor; + float3 refractedLightDirection = normalizedLightDirection; + [branch] if ((PBRFlags & TruePBR_InterlayerParallax) != 0) + { + refractedLightDirection = -refract(-normalizedLightDirection, coatModelNormal, eta); + } + float3 pbrLightColor = AdjustDirectLightColorForPBR(lightColor); + GetDirectLightInputPBR(pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor, modelNormal.xyz, coatModelNormal, refractedViewDirection, viewDirection, refractedLightDirection, normalizedLightDirection, pbrLightColor, pbrLightColor, pbrSurfaceProperties); + lightsDiffuseColor += pointDiffuseColor; + coatLightsDiffuseColor += coatPointDiffuseColor; + transmissionColor += pointTransmissionColor; + specularColorPBR += pointSpecularColor; + } +# else float lightAngle = dot(modelNormal.xyz, normalizedLightDirection.xyz); float3 lightDiffuseColor = lightColor * saturate(lightAngle.xxx); -# if defined(SOFT_LIGHTING) +# if defined(SOFT_LIGHTING) lightDiffuseColor += lightColor * GetSoftLightMultiplier(lightAngle) * rimSoftLightColor.xyz; -# endif // SOFT_LIGHTING +# endif // SOFT_LIGHTING -# if defined(RIM_LIGHTING) +# if defined(RIM_LIGHTING) lightDiffuseColor += lightColor * GetRimLightMultiplier(normalizedLightDirection, viewDirection, modelNormal.xyz) * rimSoftLightColor.xyz; -# endif // RIM_LIGHTING +# endif // RIM_LIGHTING -# if defined(BACK_LIGHTING) +# if defined(BACK_LIGHTING) lightDiffuseColor += lightColor * saturate(-lightAngle) * backLightColor.xyz; -# endif // BACK_LIGHTING +# endif // BACK_LIGHTING -# if defined(SPECULAR) || (defined(SPARKLE) && !defined(SNOW)) +# if defined(SPECULAR) || (defined(SPARKLE) && !defined(SNOW)) lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, viewDirection, modelNormal.xyz, lightColor, shininess, uv); -# endif // defined (SPECULAR) || (defined (SPARKLE) && !defined(SNOW)) +# endif // defined (SPECULAR) || (defined (SPARKLE) && !defined(SNOW)) lightsDiffuseColor += lightDiffuseColor; +# endif } # else @@ -1673,19 +2037,20 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float intensityMultiplier = 1 - intensityFactor * intensityFactor; float3 lightColor = light.color.xyz * intensityMultiplier; - float contactShadow = 1.0; + float lightShadow = 1.f; float shadowComponent = 1.0; if (PixelShaderDescriptor & _DefShadow) { if (lightIndex < numShadowLights) { shadowComponent = shadowColor[ShadowLightMaskSelect[lightIndex]]; - lightColor *= shadowComponent; + lightShadow *= shadowComponent; } } float3 normalizedLightDirection = normalize(lightDirection); float lightAngle = dot(worldSpaceNormal.xyz, normalizedLightDirection.xyz); + float contactShadow = 1; [branch] if (strictLights[0].EnableGlobalLights && !FrameParams.z && FrameParams.y && (light.firstPersonShadow || lightLimitFixSettings.EnableContactShadows) && shadowComponent != 0.0 && lightAngle > 0.0) { float3 normalizedLightDirectionVS = normalize(light.positionVS[eyeIndex].xyz - viewPosition.xyz); @@ -1701,41 +2066,70 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace } } + float3 refractedLightDirection = normalizedLightDirection; +# if defined(TRUE_PBR) + [branch] if ((PBRFlags & TruePBR_InterlayerParallax) != 0) + { + refractedLightDirection = -refract(-normalizedLightDirection, coatWorldNormal, eta); + } +# endif + + float parallaxShadow = 1; + # if defined(EMAT) - if (extendedMaterialSettings.EnableShadows && lightAngle > 0.0 && shadowComponent != 0.0 && contactShadow != 0.0) { - float3 lightDirectionTS = normalize(mul(normalizedLightDirection, tbn).xyz); + [branch] if (extendedMaterialSettings.EnableShadows && lightAngle > 0.0 && shadowComponent != 0.0 && contactShadow != 0.0) + { + float3 lightDirectionTS = normalize(mul(refractedLightDirection, tbn).xyz); # if defined(PARALLAX) - if (extendedMaterialSettings.EnableParallax) - lightColor *= GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality, screenNoise); + [branch] if (extendedMaterialSettings.EnableParallax) + parallaxShadow = GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality, screenNoise, displacementParams); # elif defined(LANDSCAPE) - if (extendedMaterialSettings.EnableTerrainParallax) - lightColor *= GetParallaxSoftShadowMultiplierTerrain(input, uv, mipLevels, lightDirectionTS, sh0, parallaxShadowQuality, screenNoise); + [branch] if (extendedMaterialSettings.EnableTerrainParallax) + parallaxShadow = GetParallaxSoftShadowMultiplierTerrain(input, uv, mipLevels, lightDirectionTS, sh0, parallaxShadowQuality, screenNoise, displacementParams); # elif defined(ENVMAP) - if (complexMaterialParallax) - lightColor *= GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, parallaxShadowQuality, screenNoise); + [branch] if (complexMaterialParallax) + parallaxShadow = GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexEnvMaskSampler, SampEnvMaskSampler, 3, parallaxShadowQuality, screenNoise, displacementParams); +# elif defined(TRUE_PBR) && !defined(LODLANDSCAPE) + [branch] if (PBRParallax) + parallaxShadow = GetParallaxSoftShadowMultiplier(uv, mipLevel, lightDirectionTS, sh0, TexParallaxSampler, SampParallaxSampler, 0, parallaxShadowQuality, screenNoise, displacementParams); # endif } # endif + float3 noParallaxShadowLightColor = lightColor * lightShadow; + lightColor = parallaxShadow * noParallaxShadowLightColor; + float3 fullShadowLightColor = lightColor * contactShadow; + +# if defined(TRUE_PBR) + { + float3 pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor; + GetDirectLightInputPBR(pointDiffuseColor, coatPointDiffuseColor, pointTransmissionColor, pointSpecularColor, worldSpaceNormal.xyz, coatWorldNormal, refractedViewDirectionWS, worldSpaceViewDirection, refractedLightDirection, normalizedLightDirection, AdjustPointLightColorForPBR(fullShadowLightColor), AdjustPointLightColorForPBR(noParallaxShadowLightColor), pbrSurfaceProperties); + lightsDiffuseColor += pointDiffuseColor; + coatLightsDiffuseColor += coatPointDiffuseColor; + transmissionColor += pointTransmissionColor; + specularColorPBR += pointSpecularColor; + } +# else float3 lightDiffuseColor = lightColor * contactShadow * saturate(lightAngle.xxx); -# if defined(SOFT_LIGHTING) +# if defined(SOFT_LIGHTING) lightDiffuseColor += lightColor * GetSoftLightMultiplier(lightAngle) * rimSoftLightColor.xyz; -# endif +# endif -# if defined(RIM_LIGHTING) +# if defined(RIM_LIGHTING) lightDiffuseColor += lightColor * GetRimLightMultiplier(normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz) * rimSoftLightColor.xyz; -# endif +# endif -# if defined(BACK_LIGHTING) +# if defined(BACK_LIGHTING) lightDiffuseColor += lightColor * saturate(-lightAngle) * backLightColor.xyz; -# endif +# endif -# if defined(SPECULAR) || (defined(SPARKLE) && !defined(SNOW)) +# if defined(SPECULAR) || (defined(SPARKLE) && !defined(SNOW)) lightsSpecularColor += GetLightSpecularInput(input, normalizedLightDirection, worldSpaceViewDirection, worldSpaceNormal.xyz, lightColor, shininess, uv); -# endif +# endif lightsDiffuseColor += lightDiffuseColor; +# endif # if defined(WETNESS_EFFECTS) if (waterRoughnessSpecular < 1.0) @@ -1754,6 +2148,9 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace saturate(dot(viewDirection, modelNormal.xyz)) * CharacterLightParams.x + CharacterLightParams.y * saturate(dot(float2(0.164398998, -0.986393988), modelNormal.yz)); float charLightColor = min(CharacterLightParams.w, max(0, CharacterLightParams.z * TexCharacterLightProjNoiseSampler.Sample(SampCharacterLightProjNoiseSampler, baseShadowUV).x)); +# if defined(TRUE_PBR) + charLightColor = AdjustPointLightColorForPBR(charLightColor); +# endif diffuseColor += (charLightMul * charLightColor).xxx; } # endif @@ -1763,13 +2160,22 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif // EYE float3 emitColor = EmitColor; -# if !defined(LANDSCAPE) - if ((0x3F & (PixelShaderDescriptor >> 24)) == _Glowmap) { +# if !defined(LANDSCAPE) && !defined(LODLANDSCAPE) + bool hasEmissive = (0x3F & (PixelShaderDescriptor >> 24)) == _Glowmap; +# if defined(TRUE_PBR) + hasEmissive = hasEmissive || (PBRFlags & TruePBR_HasEmissive != 0); +# endif + [branch] if (hasEmissive) + { float3 glowColor = TexGlowSampler.Sample(SampGlowSampler, uv).xyz; emitColor *= glowColor; } # endif +# if !defined(TRUE_PBR) + diffuseColor += emitColor.xyz; +# endif + float3 directionalAmbientColor = mul(DirectionalAmbient, modelNormal); float3 reflectionDiffuseColor = diffuseColor + directionalAmbientColor; @@ -1781,11 +2187,13 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace directionalAmbientColor *= skylightingDiffuse; # endif -# if !(defined(DEFERRED) && defined(SSGI)) - diffuseColor += directionalAmbientColor; +# if defined(TRUE_PBR) && defined(LOD_LAND_BLEND) && !defined(DEFERRED) + lodLandDiffuseColor += directionalAmbientColor; # endif - diffuseColor += emitColor.xyz; +# if !(defined(DEFERRED) && defined(SSGI)) && !defined(TRUE_PBR) + diffuseColor += directionalAmbientColor; +# endif # if defined(ENVMAP) || defined(MULTI_LAYER_PARALLAX) || defined(EYE) float envMaskColor = TexEnvMaskSampler.Sample(SampEnvMaskSampler, uv).x; @@ -1853,17 +2261,44 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # endif - float4 color; - color.xyz = diffuseColor * baseColor.xyz; - # if defined(HAIR) - float3 vertexColor = (input.Color.yyy * (TintColor.xyz - 1.0.xxx) + 1.0.xxx); + float3 vertexColor = lerp(1, TintColor.xyz, input.Color.y); # else float3 vertexColor = input.Color.xyz; # endif // defined (HAIR) - float3 realVertexColor = vertexColor; - vertexColor *= color.xyz; + float4 color = 0; + +# if defined(TRUE_PBR) + { + float3 directLightsDiffuseInput = diffuseColor * baseColor.xyz; + [branch] if ((PBRFlags & TruePBR_ColoredCoat) != 0) + { + directLightsDiffuseInput = lerp(directLightsDiffuseInput, pbrSurfaceProperties.CoatColor * coatLightsDiffuseColor, pbrSurfaceProperties.CoatStrength); + } + + color.xyz += directLightsDiffuseInput; + } + + float3 indirectDiffuseLobeWeight, indirectSpecularLobeWeight; + GetPBRIndirectLobeWeights(indirectDiffuseLobeWeight, indirectSpecularLobeWeight, worldSpaceNormal.xyz, worldSpaceViewDirection, baseColor.xyz, pbrSurfaceProperties); + +# if !defined(DEFERRED) +# if !defined(SSGI) + color.xyz += indirectDiffuseLobeWeight * directionalAmbientColor; +# endif + specularColorPBR += indirectSpecularLobeWeight * GetDynamicCubemapSpecularIrradiance(screenUV, worldSpaceNormal, worldSpaceVertexNormal, worldSpaceViewDirection, pbrSurfaceProperties.Roughness, viewPosition.z); +# else + indirectDiffuseLobeWeight *= vertexColor; +# endif + + color.xyz += emitColor.xyz; + color.xyz += transmissionColor; +# else + color.xyz += diffuseColor * baseColor.xyz; +# endif + + color.xyz *= vertexColor; # if defined(MULTI_LAYER_PARALLAX) float layerValue = MultiLayerParallaxData.x * TexLayerSampler.Sample(SampLayerSampler, uv).w; @@ -1876,7 +2311,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace float3 layerColor = TexLayerSampler.Sample(SampLayerSampler, layerUv).xyz; float mlpBlendFactor = saturate(viewNormalAngle) * (1.0 - baseColor.w); - vertexColor = lerp(vertexColor, (directionalAmbientColor + lightsDiffuseColor) * (input.Color.xyz * layerColor), mlpBlendFactor); + color.xyz = lerp(color.xyz, diffuseColor * vertexColor * layerColor, mlpBlendFactor); # if defined(DEFERRED) baseColor.xyz *= 1.0 - mlpBlendFactor; @@ -1929,24 +2364,44 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif # endif - color.xyz = vertexColor.xyz; - # if defined(EMAT) && defined(ENVMAP) specularColor *= complexSpecular; -# endif +# endif // defined (EMAT) && defined(ENVMAP) -# if !defined(DEFERRED) +# if !defined(TRUE_PBR) +# if !defined(DEFERRED) color.xyz += specularColor; -# endif // defined (EMAT) && defined(ENVMAP) +# endif color.xyz = sRGB2Lin(color.xyz); +# endif # if defined(WETNESS_EFFECTS) color.xyz += wetnessSpecular * wetnessGlossinessSpecular; # endif +# if defined(TRUE_PBR) && !defined(DEFERRED) + color.xyz += specularColorPBR; +# endif + color.xyz = Lin2sRGB(color.xyz); +# if defined(LOD_LAND_BLEND) && defined(TRUE_PBR) + { + pbrWeight = 1 - lodLandBlendFactor; + + float3 litLodLandColor = vertexColor * lodLandColor * lodLandFadeFactor * lodLandDiffuseColor; + color.xyz = lerp(color.xyz, litLodLandColor, lodLandBlendFactor); + +# if defined(DEFERRED) + specularColorPBR = lerp(specularColorPBR, 0, lodLandBlendFactor); + indirectDiffuseLobeWeight = lerp(indirectDiffuseLobeWeight, sRGB2Lin(input.Color.xyz * lodLandColor * lodLandFadeFactor), lodLandBlendFactor); + indirectSpecularLobeWeight = lerp(indirectSpecularLobeWeight, 0, lodLandBlendFactor); + pbrGlossiness = lerp(pbrGlossiness, 0, lodLandBlendFactor); +# endif + } +# endif // defined(LOD_LAND_BLEND) && defined(TRUE_PBR) + # if !defined(DEFERRED) if (FrameParams.y && FrameParams.z) color.xyz = lerp(color.xyz, input.FogParam.xyz, input.FogParam.w); @@ -1969,6 +2424,16 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace psout.Diffuse.w = 0; # else float alpha = baseColor.w; +# if defined(EMAT) && !defined(LANDSCAPE) +# if defined(PARALLAX) + alpha = TexColorSampler.Sample(SampColorSampler, uvOriginal).w; +# elif defined(TRUE_PBR) + [branch] if (PBRParallax) + { + alpha = TexColorSampler.Sample(SampColorSampler, uvOriginal).w; + } +# endif +# endif # if !defined(ADDITIONAL_ALPHA_MASK) alpha *= MaterialData.z; # else @@ -2027,27 +2492,29 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace } baseColor.xyz = 0.0; } else { - psout.Diffuse.xyz = color.xyz - tmpColor.xyz * FrameParams.zzz; + psout.Diffuse.xyz = color.xyz - preClampColor * FrameParams.z; } # else psout.Diffuse.xyz = color.xyz; # endif // defined(LIGHT_LIMIT_FIX) # if defined(SNOW) - psout.Parameters.x = dot(lightsSpecularColor, float3(0.3, 0.59, 0.11)); +# if defined(TRUE_PBR) + psout.Parameters.x = RGBToLuminanceAlternative(specularColorPBR); +# else + psout.Parameters.x = RGBToLuminanceAlternative(lightsSpecularColor); +# endif # endif // SNOW && !PBR psout.MotionVectors.xy = SSRParams.z > 1e-5 ? float2(1, 0) : screenMotionVector.xy; psout.MotionVectors.zw = float2(0, 1); # if !defined(DEFERRED) - float tmp = -1e-5 + SSRParams.x; - float tmp3 = (SSRParams.y - tmp); - float tmp2 = (glossiness - tmp); - float tmp1 = 1 / tmp3; - tmp = saturate(tmp1 * tmp2); - tmp *= tmp * (3 + -2 * tmp); - psout.ScreenSpaceNormals.w = tmp * SSRParams.w; + float ssrMask = glossiness; +# if defined(TRUE_PBR) + ssrMask = RGBToLuminanceAlternative(pbrSurfaceProperties.F0); +# endif + psout.ScreenSpaceNormals.w = smoothstep(-1e-5 + SSRParams.x, SSRParams.y, ssrMask) * SSRParams.w; // Green reflections fix if (FrameParams.z) @@ -2066,12 +2533,27 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace # endif psout.MotionVectors.zw = float2(0.0, psout.Diffuse.w); - psout.Specular = float4(specularColor.xyz, psout.Diffuse.w); - psout.Albedo = float4(baseColor.xyz * realVertexColor, psout.Diffuse.w); + + float3 outputSpecular = specularColor.xyz; +# if defined(TRUE_PBR) + outputSpecular = Lin2sRGB(specularColorPBR.xyz); +# endif + psout.Specular = float4(outputSpecular, psout.Diffuse.w); + + float3 outputAlbedo = baseColor.xyz * vertexColor; +# if defined(TRUE_PBR) + outputAlbedo = Lin2sRGB(indirectDiffuseLobeWeight); +# endif + psout.Albedo = float4(outputAlbedo, psout.Diffuse.w); float outGlossiness = saturate(glossiness * SSRParams.w); -# if defined(WETNESS_EFFECTS) +# if defined(TRUE_PBR) + psout.Parameters.z = pbrWeight; + + psout.Reflectance = float4(indirectSpecularLobeWeight, psout.Diffuse.w); + psout.NormalGlossiness = float4(EncodeNormal(screenSpaceNormal), pbrGlossiness, psout.Diffuse.w); +# elif defined(WETNESS_EFFECTS) psout.Reflectance = float4(wetnessReflectance, psout.Diffuse.w); psout.NormalGlossiness = float4(EncodeNormal(screenSpaceNormal), lerp(outGlossiness, 1.0, wetnessGlossinessSpecular), psout.Diffuse.w); # else @@ -2079,7 +2561,7 @@ PS_OUTPUT main(PS_INPUT input, bool frontFace psout.NormalGlossiness = float4(EncodeNormal(screenSpaceNormal), outGlossiness, psout.Diffuse.w); # endif -# if defined(SNOW) +# if defined(SNOW) || defined(TRUE_PBR) psout.Parameters.w = psout.Diffuse.w; # endif diff --git a/src/BSLightingShaderMaterialPBR.cpp b/src/BSLightingShaderMaterialPBR.cpp new file mode 100644 index 000000000..f7e12a8c2 --- /dev/null +++ b/src/BSLightingShaderMaterialPBR.cpp @@ -0,0 +1,314 @@ +#include "BSLightingShaderMaterialPBR.h" + +#include "State.h" + +BSLightingShaderMaterialPBR::~BSLightingShaderMaterialPBR() +{} + +BSLightingShaderMaterialPBR* BSLightingShaderMaterialPBR::Make() +{ + return new BSLightingShaderMaterialPBR; +} + +RE::BSShaderMaterial* BSLightingShaderMaterialPBR::Create() +{ + return Make(); +} + +void BSLightingShaderMaterialPBR::CopyMembers(RE::BSShaderMaterial* that) +{ + BSLightingShaderMaterialBase::CopyMembers(that); + + auto* pbrThat = static_cast(that); + + loadedWithFeature = pbrThat->loadedWithFeature; + pbrFlags = pbrThat->pbrFlags; + coatRoughness = pbrThat->coatRoughness; + coatSpecularLevel = pbrThat->coatSpecularLevel; + fuzzColor = pbrThat->fuzzColor; + fuzzWeight = pbrThat->fuzzWeight; + projectedMaterialBaseColorScale = pbrThat->projectedMaterialBaseColorScale; + projectedMaterialRoughness = pbrThat->projectedMaterialRoughness; + projectedMaterialSpecularLevel = pbrThat->projectedMaterialSpecularLevel; + + if (rmaosTexture != pbrThat->rmaosTexture) { + rmaosTexture = pbrThat->rmaosTexture; + } + if (emissiveTexture != pbrThat->emissiveTexture) { + emissiveTexture = pbrThat->emissiveTexture; + } + if (displacementTexture != pbrThat->displacementTexture) { + displacementTexture = pbrThat->displacementTexture; + } + if (featuresTexture0 != pbrThat->featuresTexture0) { + featuresTexture0 = pbrThat->featuresTexture0; + } + if (featuresTexture1 != pbrThat->featuresTexture1) { + featuresTexture1 = pbrThat->featuresTexture1; + } +} + +std::uint32_t BSLightingShaderMaterialPBR::ComputeCRC32(uint32_t srcHash) +{ + struct HashContainer + { + uint32_t pbrFlags = 0; + float coatRoughness = 0.f; + float coatSpecularLevel = 0.f; + std::array fuzzColor = { 0.f, 0.f, 0.f }; + float fuzzWeight = 0.; + std::array projectedMaterialBaseColorScale = { 0.f, 0.f, 0.f }; + float projectedMaterialRoughness = 0.f; + float projectedMaterialSpecularLevel = 0.f; + uint32_t rmaodHash = 0; + uint32_t emissiveHash = 0; + uint32_t displacementHash = 0; + uint32_t features0Hash = 0; + uint32_t features1Hash = 0; + uint32_t baseHash = 0; + } hashes; + + hashes.pbrFlags = pbrFlags.underlying(); + hashes.coatRoughness = coatRoughness * 100.f; + hashes.coatSpecularLevel = coatSpecularLevel * 100.f; + hashes.fuzzColor[0] = fuzzColor[0] * 100.f; + hashes.fuzzColor[1] = fuzzColor[1] * 100.f; + hashes.fuzzColor[2] = fuzzColor[2] * 100.f; + hashes.fuzzWeight = fuzzWeight * 100.f; + hashes.projectedMaterialBaseColorScale[0] = projectedMaterialBaseColorScale[0] * 100.f; + hashes.projectedMaterialBaseColorScale[1] = projectedMaterialBaseColorScale[1] * 100.f; + hashes.projectedMaterialBaseColorScale[2] = projectedMaterialBaseColorScale[2] * 100.f; + hashes.projectedMaterialRoughness = projectedMaterialRoughness * 100.f; + hashes.projectedMaterialSpecularLevel = projectedMaterialSpecularLevel * 100.f; + if (textureSet != nullptr) { + hashes.rmaodHash = RE::BSCRC32()(textureSet->GetTexturePath(RmaosTexture)); + hashes.emissiveHash = RE::BSCRC32()(textureSet->GetTexturePath(EmissiveTexture)); + hashes.displacementHash = RE::BSCRC32()(textureSet->GetTexturePath(DisplacementTexture)); + hashes.features0Hash = RE::BSCRC32()(textureSet->GetTexturePath(FeaturesTexture0)); + hashes.features1Hash = RE::BSCRC32()(textureSet->GetTexturePath(FeaturesTexture1)); + } + + hashes.baseHash = BSLightingShaderMaterialBase::ComputeCRC32(srcHash); + + return RE::detail::GenerateCRC32({ reinterpret_cast(&hashes), sizeof(HashContainer) }); +} + +RE::BSShaderMaterial::Feature BSLightingShaderMaterialPBR::GetFeature() const +{ + return RE::BSShaderMaterial::Feature::kDefault; + //return FEATURE; +} + +void BSLightingShaderMaterialPBR::OnLoadTextureSet(std::uint64_t arg1, RE::BSTextureSet* inTextureSet) +{ + const auto& stateData = RE::BSGraphics::State::GetSingleton()->GetRuntimeData(); + + if (diffuseTexture == nullptr || diffuseTexture == stateData.defaultTextureNormalMap) { + BSLightingShaderMaterialBase::OnLoadTextureSet(arg1, inTextureSet); + + auto* lock = &unk98; + while (_InterlockedCompareExchange(lock, 1, 0)) { + Sleep(0); + } + _mm_mfence(); + + if (inTextureSet != nullptr) { + textureSet = RE::NiPointer(inTextureSet); + } + if (textureSet != nullptr) { + textureSet->SetTexture(RmaosTexture, rmaosTexture); + textureSet->SetTexture(EmissiveTexture, emissiveTexture); + textureSet->SetTexture(DisplacementTexture, displacementTexture); + textureSet->SetTexture(FeaturesTexture0, featuresTexture0); + textureSet->SetTexture(FeaturesTexture1, featuresTexture1); + + if (auto* bgsTextureSet = netimmerse_cast(inTextureSet); bgsTextureSet != nullptr) { + if (auto* textureSetData = State::GetSingleton()->GetPBRTextureSetData(bgsTextureSet)) { + specularColorScale = textureSetData->roughnessScale; + specularPower = textureSetData->specularLevel; + rimLightPower = textureSetData->displacementScale; + + if (pbrFlags.any(PBRFlags::TwoLayer)) { + specularColor = textureSetData->coatColor; + subSurfaceLightRolloff = textureSetData->coatStrength; + coatRoughness = textureSetData->coatRoughness; + coatSpecularLevel = textureSetData->coatSpecularLevel; + } else { + if (pbrFlags.any(PBRFlags::Subsurface)) { + specularColor = textureSetData->subsurfaceColor; + subSurfaceLightRolloff = textureSetData->subsurfaceOpacity; + } + + if (pbrFlags.any(PBRFlags::Fuzz)) { + fuzzColor = textureSetData->fuzzColor; + fuzzWeight = textureSetData->fuzzWeight; + } + } + } + } + } + + if (lock != nullptr) { + *lock = 0; + _mm_mfence(); + } + } +} + +void BSLightingShaderMaterialPBR::ClearTextures() +{ + BSLightingShaderMaterialBase::ClearTextures(); + rmaosTexture.reset(); + emissiveTexture.reset(); + displacementTexture.reset(); + featuresTexture0.reset(); + featuresTexture1.reset(); +} + +void BSLightingShaderMaterialPBR::ReceiveValuesFromRootMaterial(bool skinned, bool rimLighting, bool softLighting, bool backLighting, bool MSN) +{ + BSLightingShaderMaterialBase::ReceiveValuesFromRootMaterial(skinned, rimLighting, softLighting, backLighting, MSN); + const auto& stateData = RE::BSGraphics::State::GetSingleton()->GetRuntimeData(); + if (rmaosTexture == nullptr) { + rmaosTexture = stateData.defaultTextureWhite; + } + if (emissiveTexture == nullptr) { + emissiveTexture = stateData.defaultTextureBlack; + } + if (displacementTexture == nullptr) { + displacementTexture = stateData.defaultTextureBlack; + } + if (featuresTexture0 == nullptr) { + featuresTexture0 = stateData.defaultTextureWhite; + } + if (featuresTexture1 == nullptr) { + featuresTexture1 = stateData.defaultTextureWhite; + } +} + +uint32_t BSLightingShaderMaterialPBR::GetTextures(RE::NiSourceTexture** textures) +{ + uint32_t textureIndex = 0; + if (diffuseTexture != nullptr) { + textures[textureIndex++] = diffuseTexture.get(); + } + if (normalTexture != nullptr) { + textures[textureIndex++] = normalTexture.get(); + } + if (rimSoftLightingTexture != nullptr) { + textures[textureIndex++] = rimSoftLightingTexture.get(); + } + if (specularBackLightingTexture != nullptr) { + textures[textureIndex++] = specularBackLightingTexture.get(); + } + if (rmaosTexture != nullptr) { + textures[textureIndex++] = rmaosTexture.get(); + } + if (emissiveTexture != nullptr) { + textures[textureIndex++] = emissiveTexture.get(); + } + if (displacementTexture != nullptr) { + textures[textureIndex++] = displacementTexture.get(); + } + if (featuresTexture0 != nullptr) { + textures[textureIndex++] = featuresTexture0.get(); + } + if (featuresTexture1 != nullptr) { + textures[textureIndex++] = featuresTexture1.get(); + } + + return textureIndex; +} + +void BSLightingShaderMaterialPBR::LoadBinary(RE::NiStream& stream) +{ + BSLightingShaderMaterialBase::LoadBinary(stream); + + if (loadedWithFeature == RE::BSLightingShaderMaterial::Feature::kMultilayerParallax) { + std::array parameters; + stream.iStr->read(parameters.data(), 4); + + coatRoughness = parameters[0]; + coatSpecularLevel = parameters[1]; + + fuzzColor = { parameters[0], + parameters[1], + parameters[2] }; + fuzzWeight = parameters[3]; + + if (stream.header.version > 0x4A) { + float dummy; + stream.iStr->read(&dummy, 1); + } + } +} + +float BSLightingShaderMaterialPBR::GetRoughnessScale() const +{ + return specularColorScale; +} + +float BSLightingShaderMaterialPBR::GetSpecularLevel() const +{ + return specularPower; +} + +float BSLightingShaderMaterialPBR::GetDisplacementScale() const +{ + return rimLightPower; +} + +const RE::NiColor& BSLightingShaderMaterialPBR::GetSubsurfaceColor() const +{ + return specularColor; +} + +float BSLightingShaderMaterialPBR::GetSubsurfaceOpacity() const +{ + return subSurfaceLightRolloff; +} + +const RE::NiColor& BSLightingShaderMaterialPBR::GetCoatColor() const +{ + return specularColor; +} + +float BSLightingShaderMaterialPBR::GetCoatStrength() const +{ + return subSurfaceLightRolloff; +} + +float BSLightingShaderMaterialPBR::GetCoatRoughness() const +{ + return coatRoughness; +} + +float BSLightingShaderMaterialPBR::GetCoatSpecularLevel() const +{ + return coatSpecularLevel; +} + +const std::array& BSLightingShaderMaterialPBR::GetProjectedMaterialBaseColorScale() const +{ + return projectedMaterialBaseColorScale; +} + +float BSLightingShaderMaterialPBR::GetProjectedMaterialRoughness() const +{ + return projectedMaterialRoughness; +} + +float BSLightingShaderMaterialPBR::GetProjectedMaterialSpecularLevel() const +{ + return projectedMaterialSpecularLevel; +} + +const RE::NiColor& BSLightingShaderMaterialPBR::GetFuzzColor() const +{ + return fuzzColor; +} + +float BSLightingShaderMaterialPBR::GetFuzzWeight() const +{ + return fuzzWeight; +} \ No newline at end of file diff --git a/src/BSLightingShaderMaterialPBRLandscape.cpp b/src/BSLightingShaderMaterialPBRLandscape.cpp new file mode 100644 index 000000000..9ff48cb5b --- /dev/null +++ b/src/BSLightingShaderMaterialPBRLandscape.cpp @@ -0,0 +1,138 @@ +#include "BSLightingShaderMaterialPBRLandscape.h" + +BSLightingShaderMaterialPBRLandscape::BSLightingShaderMaterialPBRLandscape() +{ + std::fill(isPbr.begin(), isPbr.end(), false); + std::fill(roughnessScales.begin(), roughnessScales.end(), 1.f); + std::fill(displacementScales.begin(), displacementScales.end(), 1.f); + std::fill(specularLevels.begin(), specularLevels.end(), 0.04f); +} + +BSLightingShaderMaterialPBRLandscape::~BSLightingShaderMaterialPBRLandscape() +{} + +BSLightingShaderMaterialPBRLandscape* BSLightingShaderMaterialPBRLandscape::Make() +{ + return new BSLightingShaderMaterialPBRLandscape; +} + +RE::BSShaderMaterial* BSLightingShaderMaterialPBRLandscape::Create() +{ + return Make(); +} + +void BSLightingShaderMaterialPBRLandscape::CopyMembers(RE::BSShaderMaterial* that) +{ + BSLightingShaderMaterialBase::CopyMembers(that); + + auto* pbrThat = static_cast(that); + + pbrThat->numLandscapeTextures = numLandscapeTextures; + + for (uint32_t textureIndex = 0; textureIndex < NumTiles; ++textureIndex) { + pbrThat->landscapeBaseColorTextures[textureIndex] = landscapeBaseColorTextures[textureIndex]; + pbrThat->landscapeNormalTextures[textureIndex] = landscapeNormalTextures[textureIndex]; + pbrThat->landscapeDisplacementTextures[textureIndex] = landscapeDisplacementTextures[textureIndex]; + pbrThat->landscapeRMAOSTextures[textureIndex] = landscapeRMAOSTextures[textureIndex]; + } + pbrThat->terrainOverlayTexture = terrainOverlayTexture; + pbrThat->terrainNoiseTexture = terrainNoiseTexture; + pbrThat->landBlendParams = landBlendParams; + pbrThat->isPbr = isPbr; + pbrThat->roughnessScales = roughnessScales; + pbrThat->displacementScales = displacementScales; + pbrThat->specularLevels = specularLevels; + pbrThat->terrainTexOffsetX = terrainTexOffsetX; + pbrThat->terrainTexOffsetY = terrainTexOffsetY; + pbrThat->terrainTexFade = terrainTexFade; +} + +RE::BSShaderMaterial::Feature BSLightingShaderMaterialPBRLandscape::GetFeature() const +{ + return RE::BSShaderMaterial::Feature::kMultiTexLandLODBlend; + //return FEATURE; +} + +void BSLightingShaderMaterialPBRLandscape::ClearTextures() +{ + BSLightingShaderMaterialBase::ClearTextures(); + for (auto& texture : landscapeBaseColorTextures) { + texture.reset(); + } + for (auto& texture : landscapeNormalTextures) { + texture.reset(); + } + for (auto& texture : landscapeDisplacementTextures) { + texture.reset(); + } + for (auto& texture : landscapeRMAOSTextures) { + texture.reset(); + } + terrainOverlayTexture.reset(); + terrainNoiseTexture.reset(); +} + +void BSLightingShaderMaterialPBRLandscape::ReceiveValuesFromRootMaterial(bool skinned, bool rimLighting, bool softLighting, bool backLighting, bool MSN) +{ + BSLightingShaderMaterialBase::ReceiveValuesFromRootMaterial(skinned, rimLighting, softLighting, backLighting, MSN); + const auto& stateData = RE::BSGraphics::State::GetSingleton()->GetRuntimeData(); + if (terrainOverlayTexture == nullptr) { + terrainOverlayTexture = stateData.defaultTextureNormalMap; + } + if (terrainNoiseTexture == nullptr) { + terrainNoiseTexture = stateData.defaultTextureNormalMap; + } + for (uint32_t textureIndex = 0; textureIndex < numLandscapeTextures; ++textureIndex) { + if (landscapeBaseColorTextures[textureIndex] == nullptr) { + landscapeBaseColorTextures[textureIndex] = stateData.defaultTextureBlack; + } + if (landscapeNormalTextures[textureIndex] == nullptr) { + landscapeNormalTextures[textureIndex] = stateData.defaultTextureNormalMap; + } + if (landscapeDisplacementTextures[textureIndex] == nullptr) { + landscapeDisplacementTextures[textureIndex] = stateData.defaultTextureBlack; + } + if (landscapeRMAOSTextures[textureIndex] == nullptr) { + landscapeRMAOSTextures[textureIndex] = stateData.defaultTextureWhite; + } + } +} + +uint32_t BSLightingShaderMaterialPBRLandscape::GetTextures(RE::NiSourceTexture** textures) +{ + uint32_t textureIndex = 0; + if (diffuseTexture != nullptr) { + textures[textureIndex++] = diffuseTexture.get(); + } + if (normalTexture != nullptr) { + textures[textureIndex++] = normalTexture.get(); + } + if (rimSoftLightingTexture != nullptr) { + textures[textureIndex++] = rimSoftLightingTexture.get(); + } + if (specularBackLightingTexture != nullptr) { + textures[textureIndex++] = specularBackLightingTexture.get(); + } + for (uint32_t tileIndex = 0; tileIndex < numLandscapeTextures; ++tileIndex) { + if (landscapeBaseColorTextures[tileIndex] != nullptr) { + textures[textureIndex++] = landscapeBaseColorTextures[tileIndex].get(); + } + if (landscapeNormalTextures[tileIndex] != nullptr) { + textures[textureIndex++] = landscapeNormalTextures[tileIndex].get(); + } + if (landscapeDisplacementTextures[tileIndex] != nullptr) { + textures[textureIndex++] = landscapeDisplacementTextures[tileIndex].get(); + } + if (landscapeRMAOSTextures[tileIndex] != nullptr) { + textures[textureIndex++] = landscapeRMAOSTextures[tileIndex].get(); + } + } + if (terrainOverlayTexture != nullptr) { + textures[textureIndex++] = terrainOverlayTexture.get(); + } + if (terrainNoiseTexture != nullptr) { + textures[textureIndex++] = terrainNoiseTexture.get(); + } + + return textureIndex; +} \ No newline at end of file diff --git a/src/Deferred.cpp b/src/Deferred.cpp index be7627d90..ff6e51690 100644 --- a/src/Deferred.cpp +++ b/src/Deferred.cpp @@ -434,12 +434,13 @@ void Deferred::DeferredPasses() ID3D11Buffer* buffer = skylighting->loaded ? skylighting->skylightingCB->CB() : nullptr; context->CSSetConstantBuffers(1, 1, &buffer); - ID3D11ShaderResourceView* srvs[5]{ + ID3D11ShaderResourceView* srvs[6]{ albedo.SRV, normalRoughness.SRV, skylighting->loaded ? depth.depthSRV : nullptr, skylighting->loaded ? skylighting->texProbeArray->srv.get() : nullptr, ssgi->loaded ? ssgi->texGI[ssgi->outputGIIdx]->srv.get() : nullptr, + masks2.SRV, }; context->CSSetShaderResources(0, ARRAYSIZE(srvs), srvs); diff --git a/src/FeatureBuffer.cpp b/src/FeatureBuffer.cpp index e56dadfb8..d93d5da25 100644 --- a/src/FeatureBuffer.cpp +++ b/src/FeatureBuffer.cpp @@ -33,5 +33,6 @@ std::pair GetFeatureBufferData() TerrainOcclusion::GetSingleton()->GetCommonBufferData(), WetnessEffects::GetSingleton()->GetCommonBufferData(), LightLimitFix::GetSingleton()->GetCommonBufferData(), - Skylighting::GetSingleton()->cbData); + Skylighting::GetSingleton()->cbData, + State::GetSingleton()->pbrSettings); } \ No newline at end of file diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 91391a713..bb5ed7335 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -2,6 +2,8 @@ #include +#include "BSLightingShaderMaterialPBR.h" +#include "BSLightingShaderMaterialPBRLandscape.h" #include "Menu.h" #include "ShaderCache.h" #include "State.h" @@ -82,12 +84,158 @@ void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream); decltype(&hk_BSShader_LoadShaders) ptr_BSShader_LoadShaders; +namespace Permutations +{ + template + std::unordered_set GenerateFlagPermutations(const RangeType& flags, uint32_t constantFlags) + { + std::vector flagValues; + std::ranges::transform(flags, std::back_inserter(flagValues), [](auto flag) { return static_cast(flag); }); + const uint32_t size = static_cast(flagValues.size()); + + std::unordered_set result; + for (uint32_t mask = 0; mask < (1u << size); ++mask) { + uint32_t flag = constantFlags; + for (size_t index = 0; index < size; ++index) { + if (mask & (1 << index)) { + flag |= flagValues[index]; + } + } + result.insert(flag); + } + + return result; + } + + uint32_t GetLightingShaderDescriptor(SIE::ShaderCache::LightingShaderTechniques technique, uint32_t flags) + { + return ((static_cast(technique) & 0x3F) << 24) | flags; + } + + void AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques technique, const std::unordered_set& flags, std::unordered_set& result) + { + for (uint32_t flag : flags) { + result.insert(GetLightingShaderDescriptor(technique, flag)); + } + } + + std::unordered_set GeneratePBRLightingVertexPermutations() + { + using enum SIE::ShaderCache::LightingShaderFlags; + + constexpr std::array defaultFlags{ VC, Skinned, WorldMap }; + constexpr std::array projectedUvFlags{ VC, WorldMap }; + constexpr std::array treeFlags{ VC, Skinned }; + constexpr std::array landFlags{ VC }; + + constexpr uint32_t defaultConstantFlags = static_cast(TruePbr); + constexpr uint32_t projectedUvConstantFlags = static_cast(TruePbr) | static_cast(ProjectedUV); + + const std::unordered_set defaultFlagValues = GenerateFlagPermutations(defaultFlags, defaultConstantFlags); + const std::unordered_set projectedUvFlagValues = GenerateFlagPermutations(projectedUvFlags, projectedUvConstantFlags); + const std::unordered_set treeFlagValues = GenerateFlagPermutations(treeFlags, defaultConstantFlags); + const std::unordered_set landFlagValues = GenerateFlagPermutations(landFlags, defaultConstantFlags); + + std::unordered_set result; + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, defaultFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, projectedUvFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::TreeAnim, treeFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLand, landFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result); + return result; + } + + std::unordered_set GeneratePBRLightingPixelPermutations() + { + using enum SIE::ShaderCache::LightingShaderFlags; + + constexpr std::array defaultFlags{ Skinned, DoAlphaTest, AdditionalAlphaMask }; + constexpr std::array projectedUvFlags{ DoAlphaTest, AdditionalAlphaMask, Snow, BaseObjectIsSnow }; + constexpr std::array lodObjectsFlags{ WorldMap, DoAlphaTest, AdditionalAlphaMask, ProjectedUV }; + constexpr std::array treeFlags{ Skinned, DoAlphaTest, AdditionalAlphaMask }; + + constexpr uint32_t defaultConstantFlags = static_cast(TruePbr) | static_cast(VC); + constexpr uint32_t projectedUvConstantFlags = static_cast(TruePbr) | static_cast(VC) | static_cast(ProjectedUV); + + const std::unordered_set defaultFlagValues = GenerateFlagPermutations(defaultFlags, defaultConstantFlags); + const std::unordered_set projectedUvFlagValues = GenerateFlagPermutations(projectedUvFlags, projectedUvConstantFlags); + const std::unordered_set lodObjectsFlagValues = GenerateFlagPermutations(lodObjectsFlags, defaultConstantFlags); + const std::unordered_set treeFlagValues = GenerateFlagPermutations(treeFlags, defaultConstantFlags); + const std::unordered_set landFlagValues = { defaultConstantFlags }; + + std::unordered_set result; + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, defaultFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, projectedUvFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::LODObjects, lodObjectsFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::LODObjectHD, lodObjectsFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::TreeAnim, treeFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLand, landFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result); + return result; + } + + std::unordered_set GeneratePBRGrassPermutations() + { + using enum SIE::ShaderCache::GrassShaderTechniques; + using enum SIE::ShaderCache::GrassShaderFlags; + + return { static_cast(TruePbr), + static_cast(TruePbr) | static_cast(AlphaTest) }; + } + + std::unordered_set GeneratePBRGrassVertexPermutations() + { + return GeneratePBRGrassPermutations(); + } + + std::unordered_set GeneratePBRGrassPixelPermutations() + { + return GeneratePBRGrassPermutations(); + } +} + void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream) { (ptr_BSShader_LoadShaders)(shader, stream); auto& shaderCache = SIE::ShaderCache::Instance(); if (shaderCache.IsDiskCache() || shaderCache.IsDump()) { + if (shaderCache.IsDiskCache() && shader->shaderType == RE::BSShader::Type::Lighting) { + const auto vertexPermutations = Permutations::GeneratePBRLightingVertexPermutations(); + for (auto descriptor : vertexPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetVertexShader(*shader, vertexShaderDesriptor); + } + + const auto pixelPermutations = Permutations::GeneratePBRLightingPixelPermutations(); + for (auto descriptor : pixelPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetPixelShader(*shader, pixelShaderDescriptor); + } + } + + if (shaderCache.IsDiskCache() && shader->shaderType == RE::BSShader::Type::Grass) { + const auto vertexPermutations = Permutations::GeneratePBRGrassVertexPermutations(); + for (auto descriptor : vertexPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetVertexShader(*shader, vertexShaderDesriptor); + } + + const auto pixelPermutations = Permutations::GeneratePBRGrassPixelPermutations(); + for (auto descriptor : pixelPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetPixelShader(*shader, pixelShaderDescriptor); + } + } + for (const auto& entry : shader->vertexShaders) { if (entry->shader && shaderCache.IsDump()) { auto& bytecode = GetShaderBytecode(entry->shader); @@ -133,12 +281,30 @@ bool hk_BSShader_BeginTechnique(RE::BSShader* shader, uint32_t vertexDescriptor, state->ModifyShaderLookup(*shader, state->modifiedVertexDescriptor, state->modifiedPixelDescriptor); - auto ret = (ptr_BSShader_BeginTechnique)(shader, vertexDescriptor, pixelDescriptor, skipPixelShader); + bool shaderFound = (ptr_BSShader_BeginTechnique)(shader, vertexDescriptor, pixelDescriptor, skipPixelShader); + + if (!shaderFound) { + auto& shaderCache = SIE::ShaderCache::Instance(); + RE::BSGraphics::VertexShader* vertexShader = shaderCache.GetVertexShader(*shader, state->modifiedVertexDescriptor); + RE::BSGraphics::PixelShader* pixelShader = shaderCache.GetPixelShader(*shader, state->modifiedPixelDescriptor); + if (vertexShader == nullptr || (!skipPixelShader && pixelShader == nullptr)) { + shaderFound = false; + } else { + state->settingCustomShader = true; + RE::BSGraphics::RendererShadowState::GetSingleton()->SetVertexShader(vertexShader); + if (skipPixelShader) { + pixelShader = nullptr; + } + RE::BSGraphics::RendererShadowState::GetSingleton()->SetPixelShader(pixelShader); + state->settingCustomShader = false; + shaderFound = true; + } + } state->lastModifiedVertexDescriptor = state->modifiedVertexDescriptor; state->lastModifiedPixelDescriptor = state->modifiedPixelDescriptor; - return ret; + return shaderFound; } decltype(&IDXGISwapChain::Present) ptr_IDXGISwapChain_Present; @@ -150,6 +316,30 @@ HRESULT WINAPI hk_IDXGISwapChain_Present(IDXGISwapChain* This, UINT SyncInterval return (This->*ptr_IDXGISwapChain_Present)(SyncInterval, Flags); } +struct ExtendedRendererState +{ + static constexpr uint32_t NumPSTextures = 12; + static constexpr uint32_t FirstPSTexture = 80; + + uint32_t PSResourceModifiedBits = 0; + ID3D11ShaderResourceView* PSTexture[NumPSTextures]; + + void SetPSTexture(size_t textureIndex, RE::BSGraphics::Texture* newTexture) + { + ID3D11ShaderResourceView* resourceView = newTexture ? newTexture->resourceView : nullptr; + //if (PSTexture[textureIndex] != resourceView) + { + PSTexture[textureIndex] = resourceView; + PSResourceModifiedBits |= (1 << textureIndex); + } + } + + ExtendedRendererState() + { + std::fill_n(PSTexture, NumPSTextures, nullptr); + } +} extendedRendererState; + void hk_BSGraphics_SetDirtyStates(bool isCompute); decltype(&hk_BSGraphics_SetDirtyStates) ptr_BSGraphics_SetDirtyStates; @@ -157,6 +347,17 @@ decltype(&hk_BSGraphics_SetDirtyStates) ptr_BSGraphics_SetDirtyStates; void hk_BSGraphics_SetDirtyStates(bool isCompute) { (ptr_BSGraphics_SetDirtyStates)(isCompute); + + { + auto context = State::GetSingleton()->context; + for (uint32_t textureIndex = 0; textureIndex < ExtendedRendererState::NumPSTextures; ++textureIndex) { + if (extendedRendererState.PSResourceModifiedBits & (1 << textureIndex)) { + context->PSSetShaderResources(ExtendedRendererState::FirstPSTexture + textureIndex, 1, &extendedRendererState.PSTexture[textureIndex]); + } + } + extendedRendererState.PSResourceModifiedBits = 0; + } + State::GetSingleton()->Draw(); } @@ -251,12 +452,15 @@ void hk_PollInputDevices(RE::BSTEventSource* a_dispatcher, RE:: if (*a_events) { if (auto device = (*a_events)->GetDevice()) { // Check that the device is not a Gamepad or VR controller. If it is, unblock input. - auto vrDevice = (REL::Module::IsVR() && ((device == RE::INPUT_DEVICES::INPUT_DEVICE::kVivePrimary) || - (device == RE::INPUT_DEVICES::INPUT_DEVICE::kViveSecondary) || - (device == RE::INPUT_DEVICES::INPUT_DEVICE::kOculusPrimary) || - (device == RE::INPUT_DEVICES::INPUT_DEVICE::kOculusSecondary) || - (device == RE::INPUT_DEVICES::INPUT_DEVICE::kWMRPrimary) || - (device == RE::INPUT_DEVICES::INPUT_DEVICE::kWMRSecondary))); + bool vrDevice = false; +#ifdef ENABLE_SKYRIM_VR + vrDevice = (REL::Module::IsVR() && ((device == RE::INPUT_DEVICES::INPUT_DEVICE::kVivePrimary) || + (device == RE::INPUT_DEVICES::INPUT_DEVICE::kViveSecondary) || + (device == RE::INPUT_DEVICES::INPUT_DEVICE::kOculusPrimary) || + (device == RE::INPUT_DEVICES::INPUT_DEVICE::kOculusSecondary) || + (device == RE::INPUT_DEVICES::INPUT_DEVICE::kWMRPrimary) || + (device == RE::INPUT_DEVICES::INPUT_DEVICE::kWMRSecondary))); +#endif blockedDevice = !((device == RE::INPUT_DEVICES::INPUT_DEVICE::kGamepad) || vrDevice); } } @@ -340,6 +544,600 @@ namespace Hooks static inline REL::Relocation func; }; + struct BSLightingShaderProperty_LoadBinary + { + static void thunk(RE::BSLightingShaderProperty* property, RE::NiStream& stream) + { + using enum RE::BSShaderProperty::EShaderPropertyFlag; + + RE::BSShaderMaterial::Feature feature = RE::BSShaderMaterial::Feature::kDefault; + stream.iStr->read(&feature, 1); + + { + auto vtable = REL::Relocation(RE::NiShadeProperty::VTABLE[0]); + auto baseMethod = reinterpret_cast((vtable.get()[0x18])); + baseMethod(property, stream); + } + + stream.iStr->read(&property->flags, 1); + + bool isPbr = false; + { + RE::BSLightingShaderMaterialBase* material = nullptr; + if (property->flags.any(kMenuScreen)) { + auto* pbrMaterial = BSLightingShaderMaterialPBR::Make(); + pbrMaterial->loadedWithFeature = feature; + material = pbrMaterial; + isPbr = true; + } else { + material = RE::BSLightingShaderMaterialBase::CreateMaterial(feature); + } + property->LinkMaterial(nullptr, false); + property->material = material; + } + + { + stream.iStr->read(&property->material->texCoordOffset[0].x, 1); + stream.iStr->read(&property->material->texCoordOffset[0].y, 1); + stream.iStr->read(&property->material->texCoordScale[0].x, 1); + stream.iStr->read(&property->material->texCoordScale[0].y, 1); + + property->material->texCoordOffset[1] = property->material->texCoordOffset[0]; + property->material->texCoordScale[1] = property->material->texCoordScale[0]; + } + + stream.LoadLinkID(); + + { + RE::NiColor emissiveColor{}; + stream.iStr->read(&emissiveColor.red, 1); + stream.iStr->read(&emissiveColor.green, 1); + stream.iStr->read(&emissiveColor.blue, 1); + + if (property->emissiveColor != nullptr && property->flags.any(kOwnEmit)) { + *property->emissiveColor = emissiveColor; + } + } + + stream.iStr->read(&property->emissiveMult, 1); + + static_cast(property->material)->LoadBinary(stream); + + if (isPbr) { + auto pbrMaterial = static_cast(property->material); + if (property->flags.any(kMultiLayerParallax)) { + pbrMaterial->pbrFlags.set(PBRFlags::TwoLayer); + if (property->flags.any(kSoftLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::InterlayerParallax); + } + if (property->flags.any(kBackLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::CoatNormal); + } + if (property->flags.any(kEffectLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::ColoredCoat); + } + } else if (property->flags.any(kBackLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::HairMarschner); + } else { + if (property->flags.any(kRimLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::Subsurface); + } + if (property->flags.any(kSoftLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::Fuzz); + } + } + property->flags.set(kVertexLighting); + property->flags.reset(kMenuScreen, kSpecular, kGlowMap, kEnvMap, kMultiLayerParallax, kSoftLighting, kRimLighting, kBackLighting, kAnisotropicLighting, kEffectLighting); + } + } + static inline REL::Relocation func; + }; + + struct BSLightingShaderProperty_GetRenderPasses + { + static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator) + { + auto renderPasses = func(property, geometry, renderFlags, accumulator); + if (renderPasses == nullptr) { + return renderPasses; + } + + bool isPbr = false; + + if (property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting) && (property->material->GetFeature() == RE::BSShaderMaterial::Feature::kDefault || property->material->GetFeature() == RE::BSShaderMaterial::Feature::kMultiTexLandLODBlend)) { + isPbr = true; + } + + auto currentPass = renderPasses->head; + while (currentPass != nullptr) { + if (currentPass->shader->shaderType == RE::BSShader::Type::Lighting) { + constexpr uint32_t LightingTechniqueStart = 0x4800002D; + auto lightingTechnique = currentPass->passEnum - LightingTechniqueStart; + auto lightingFlags = lightingTechnique & ~(~0u << 24); + auto lightingType = static_cast((lightingTechnique >> 24) & 0x3F); + lightingFlags &= ~0b111000u; + if (isPbr) { + lightingFlags |= static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr); + } + lightingTechnique = (static_cast(lightingType) << 24) | lightingFlags; + currentPass->passEnum = lightingTechnique + LightingTechniqueStart; + } + currentPass = currentPass->next; + } + + return renderPasses; + } + static inline REL::Relocation func; + }; + + struct BSLightingShader_SetupMaterial + { + static void thunk(RE::BSLightingShader* shader, RE::BSLightingShaderMaterialBase const* material) + { + using enum SIE::ShaderCache::LightingShaderTechniques; + + auto lightingFlags = shader->currentRawTechnique & ~(~0u << 24); + auto lightingType = static_cast((shader->currentRawTechnique >> 24) & 0x3F); + if (!(lightingType == LODLand || lightingType == LODLandNoise) && (lightingFlags & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr))) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + auto renderer = RE::BSGraphics::Renderer::GetSingleton(); + + RE::BSGraphics::Renderer::PrepareVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + + if (lightingType == MTLand || lightingType == MTLandLODBlend) { + auto* pbrMaterial = static_cast(material); + + constexpr size_t NormalStartIndex = 7; + + if (pbrMaterial->diffuseTexture != nullptr) { + shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); + } + if (pbrMaterial->normalTexture != nullptr) { + shadowState->SetPSTexture(NormalStartIndex, pbrMaterial->normalTexture->rendererTexture); + } + if (pbrMaterial->landscapeDisplacementTextures[0] != nullptr) { + extendedRendererState.SetPSTexture(0, pbrMaterial->landscapeDisplacementTextures[0]->rendererTexture); + } + if (pbrMaterial->landscapeRMAOSTextures[0] != nullptr) { + extendedRendererState.SetPSTexture(BSLightingShaderMaterialPBRLandscape::NumTiles, pbrMaterial->landscapeRMAOSTextures[0]->rendererTexture); + } + for (uint32_t textureIndex = 1; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { + if (pbrMaterial->landscapeBaseColorTextures[textureIndex - 1] != nullptr) { + shadowState->SetPSTexture(textureIndex, pbrMaterial->landscapeBaseColorTextures[textureIndex - 1]->rendererTexture); + } + if (pbrMaterial->landscapeNormalTextures[textureIndex - 1] != nullptr) { + shadowState->SetPSTexture(NormalStartIndex + textureIndex, pbrMaterial->landscapeNormalTextures[textureIndex - 1]->rendererTexture); + } + if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr) { + extendedRendererState.SetPSTexture(textureIndex, pbrMaterial->landscapeDisplacementTextures[textureIndex]->rendererTexture); + } + if (pbrMaterial->landscapeRMAOSTextures[textureIndex] != nullptr) { + extendedRendererState.SetPSTexture(BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex, pbrMaterial->landscapeRMAOSTextures[textureIndex]->rendererTexture); + } + } + + shadowState->SetPSTextureAddressMode(0, RE::BSGraphics::TextureAddressMode::kWrapSWrapT); + shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + if (pbrMaterial->terrainOverlayTexture != nullptr) { + shadowState->SetPSTexture(13, pbrMaterial->terrainOverlayTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(13, RE::BSGraphics::TextureAddressMode::kClampSClampT); + shadowState->SetPSTextureFilterMode(13, RE::BSGraphics::TextureFilterMode::kAnisotropic); + } + + if (pbrMaterial->terrainNoiseTexture != nullptr) { + shadowState->SetPSTexture(15, pbrMaterial->terrainNoiseTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(15, RE::BSGraphics::TextureAddressMode::kWrapSWrapT); + shadowState->SetPSTextureFilterMode(15, RE::BSGraphics::TextureFilterMode::kBilinear); + } + + { + uint32_t flags = 0; + for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { + if (pbrMaterial->isPbr[textureIndex]) { + flags |= (1 << textureIndex); + if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr && pbrMaterial->landscapeDisplacementTextures[textureIndex] != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack) { + flags |= (1 << (BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex)); + } + } + } + shadowState->SetPSConstant(flags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36); + } + + { + constexpr size_t PBRParamsStartIndex = 37; + + for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { + std::array PBRParams; + PBRParams[0] = pbrMaterial->roughnessScales[textureIndex]; + PBRParams[1] = pbrMaterial->displacementScales[textureIndex]; + PBRParams[2] = pbrMaterial->specularLevels[textureIndex]; + shadowState->SetPSConstant(PBRParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, PBRParamsStartIndex + textureIndex); + } + } + + { + std::array lodTexParams; + lodTexParams[0] = pbrMaterial->terrainTexOffsetX; + lodTexParams[1] = pbrMaterial->terrainTexOffsetY; + lodTexParams[2] = 1.f; + lodTexParams[3] = pbrMaterial->terrainTexFade; + shadowState->SetPSConstant(lodTexParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 24); + } + } else if (lightingType == None || lightingType == TreeAnim) { + auto* pbrMaterial = static_cast(material); + if (pbrMaterial->diffuseRenderTargetSourceIndex != -1) { + shadowState->SetPSTexture(0, renderer->GetRuntimeData().renderTargets[pbrMaterial->diffuseRenderTargetSourceIndex]); + } else { + shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); + } + shadowState->SetPSTextureAddressMode(0, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(1, pbrMaterial->normalTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(1, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(5, pbrMaterial->rmaosTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(5, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(5, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + stl::enumeration shaderFlags; + if (pbrMaterial->pbrFlags.any(PBRFlags::TwoLayer)) { + shaderFlags.set(PBRShaderFlags::TwoLayer); + if (pbrMaterial->pbrFlags.any(PBRFlags::InterlayerParallax)) { + shaderFlags.set(PBRShaderFlags::InterlayerParallax); + } + if (pbrMaterial->pbrFlags.any(PBRFlags::CoatNormal)) { + shaderFlags.set(PBRShaderFlags::CoatNormal); + } + if (pbrMaterial->pbrFlags.any(PBRFlags::ColoredCoat)) { + shaderFlags.set(PBRShaderFlags::ColoredCoat); + } + + std::array PBRParams2; + PBRParams2[0] = pbrMaterial->GetCoatColor().red; + PBRParams2[1] = pbrMaterial->GetCoatColor().green; + PBRParams2[2] = pbrMaterial->GetCoatColor().blue; + PBRParams2[3] = pbrMaterial->GetCoatStrength(); + shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 43); + + std::array PBRParams3; + PBRParams3[0] = pbrMaterial->GetCoatRoughness(); + PBRParams3[1] = pbrMaterial->GetCoatSpecularLevel(); + shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27); + } else if (pbrMaterial->pbrFlags.any(PBRFlags::HairMarschner)) { + shaderFlags.set(PBRShaderFlags::HairMarschner); + } else { + if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) { + shaderFlags.set(PBRShaderFlags::Subsurface); + + std::array PBRParams2; + PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red; + PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green; + PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue; + PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity(); + shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 43); + } + if (pbrMaterial->pbrFlags.any(PBRFlags::Fuzz)) { + shaderFlags.set(PBRShaderFlags::Fuzz); + + std::array PBRParams3; + PBRParams3[0] = pbrMaterial->GetFuzzColor().red; + PBRParams3[1] = pbrMaterial->GetFuzzColor().green; + PBRParams3[2] = pbrMaterial->GetFuzzColor().blue; + PBRParams3[3] = pbrMaterial->GetFuzzWeight(); + shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27); + } + } + + { + std::array PBRProjectedUVParams1; + PBRProjectedUVParams1[0] = pbrMaterial->GetProjectedMaterialBaseColorScale()[0]; + PBRProjectedUVParams1[1] = pbrMaterial->GetProjectedMaterialBaseColorScale()[1]; + PBRProjectedUVParams1[2] = pbrMaterial->GetProjectedMaterialBaseColorScale()[2]; + shadowState->SetPSConstant(PBRProjectedUVParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 21); + + std::array PBRProjectedUVParams2; + PBRProjectedUVParams2[0] = pbrMaterial->GetProjectedMaterialRoughness(); + PBRProjectedUVParams2[1] = pbrMaterial->GetProjectedMaterialSpecularLevel(); + shadowState->SetPSConstant(PBRProjectedUVParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 22); + } + + const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + if (hasEmissive) { + shadowState->SetPSTexture(6, pbrMaterial->emissiveTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(6, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(6, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasEmissive); + } + + const bool hasDisplacement = pbrMaterial->displacementTexture != nullptr && pbrMaterial->displacementTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + if (hasDisplacement) { + shadowState->SetPSTexture(4, pbrMaterial->displacementTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(4, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasDisplacement); + } + + const bool hasFeaturesTexture0 = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; + if (hasFeaturesTexture0) { + shadowState->SetPSTexture(12, pbrMaterial->featuresTexture0->rendererTexture); + shadowState->SetPSTextureAddressMode(12, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(12, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0); + } + + const bool hasFeaturesTexture1 = pbrMaterial->featuresTexture1 != nullptr && pbrMaterial->featuresTexture1 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; + if (hasFeaturesTexture1) { + shadowState->SetPSTexture(9, pbrMaterial->featuresTexture1->rendererTexture); + shadowState->SetPSTextureAddressMode(9, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(9, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasFeaturesTexture1); + } + + { + shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36); + } + + { + std::array PBRParams1; + PBRParams1[0] = pbrMaterial->GetRoughnessScale(); + PBRParams1[1] = pbrMaterial->GetDisplacementScale(); + PBRParams1[2] = pbrMaterial->GetSpecularLevel(); + shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 37); + } + } + + { + const uint32_t bufferIndex = RE::BSShaderManager::State::GetSingleton().textureTransformCurrentBuffer; + + std::array texCoordOffsetScale; + texCoordOffsetScale[0] = material->texCoordOffset[bufferIndex].x; + texCoordOffsetScale[1] = material->texCoordOffset[bufferIndex].y; + texCoordOffsetScale[2] = material->texCoordScale[bufferIndex].x; + texCoordOffsetScale[3] = material->texCoordScale[bufferIndex].y; + shadowState->SetVSConstant(texCoordOffsetScale, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 11); + } + + if (lightingFlags & static_cast(SIE::ShaderCache::LightingShaderFlags::CharacterLight)) { + static const REL::Relocation characterLightTexture{ RELOCATION_ID(513464, 391302) }; + + if (characterLightTexture->renderTarget >= RE::RENDER_TARGET::kFRAMEBUFFER) { + shadowState->SetPSTexture(11, renderer->GetRuntimeData().renderTargets[characterLightTexture->renderTarget]); + shadowState->SetPSTextureAddressMode(11, RE::BSGraphics::TextureAddressMode::kClampSClampT); + } + + const auto& smState = RE::BSShaderManager::State::GetSingleton(); + std::array characterLightParams; + if (smState.characterLightEnabled) { + std::copy_n(smState.characterLightParams, 4, characterLightParams.data()); + } else { + std::fill_n(characterLightParams.data(), 4, 0.f); + } + shadowState->SetPSConstant(characterLightParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 35); + } + + RE::BSGraphics::Renderer::FlushVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::ApplyVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + } else { + func(shader, material); + } + }; + static inline REL::Relocation func; + }; + + struct BSLightingShader_SetupGeometry + { + static void thunk(RE::BSLightingShader* shader, RE::BSRenderPass* pass, uint32_t renderFlags) + { + const uint32_t originalExtraFlags = shader->currentRawTechnique & 0b111000u; + + if ((shader->currentRawTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) { + shader->currentRawTechnique |= static_cast(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular); + } + + shader->currentRawTechnique &= ~0b111000u; + shader->currentRawTechnique |= ((pass->numLights - 1) << 3); + + func(shader, pass, renderFlags); + + shader->currentRawTechnique &= ~0b111000u; + shader->currentRawTechnique |= originalExtraFlags; + + if ((shader->currentRawTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) { + shader->currentRawTechnique &= ~static_cast(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular); + } + } + static inline REL::Relocation func; + }; + + uint32_t hk_BSLightingShader_GetPixelTechnique(uint32_t rawTechnique) + { + uint32_t pixelTechnique = rawTechnique; + + pixelTechnique &= ~0b111000000u; + if ((pixelTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::ModelSpaceNormals)) == 0) { + pixelTechnique &= ~static_cast(SIE::ShaderCache::LightingShaderFlags::Skinned); + } + pixelTechnique |= static_cast(SIE::ShaderCache::LightingShaderFlags::VC); + + return pixelTechnique; + } + + void SetupLandscapeTexture(BSLightingShaderMaterialPBRLandscape& material, RE::TESLandTexture& landTexture, uint32_t textureIndex) + { + if (textureIndex >= 6) { + return; + } + + auto textureSet = landTexture.textureSet; + if (textureSet == nullptr) { + return; + } + + auto* textureSetData = State::GetSingleton()->GetPBRTextureSetData(landTexture.textureSet); + const bool isPbr = textureSetData != nullptr; + + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::BaseColorTexture, textureIndex == 0 ? material.diffuseTexture : material.landscapeBaseColorTextures[textureIndex - 1]); + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::NormalTexture, textureIndex == 0 ? material.normalTexture : material.landscapeNormalTextures[textureIndex - 1]); + + if (isPbr) { + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::RmaosTexture, material.landscapeRMAOSTextures[textureIndex]); + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::DisplacementTexture, material.landscapeDisplacementTextures[textureIndex]); + material.displacementScales[textureIndex] = textureSetData->displacementScale; + material.roughnessScales[textureIndex] = textureSetData->roughnessScale; + material.specularLevels[textureIndex] = textureSetData->specularLevel; + } + material.isPbr[textureIndex] = isPbr; + + if (textureIndex == 0) { + if (material.diffuseTexture != nullptr) { + material.numLandscapeTextures = std::max(material.numLandscapeTextures, 1u); + } + } else { + if (material.landscapeBaseColorTextures[textureIndex] != nullptr) { + material.numLandscapeTextures = std::max(material.numLandscapeTextures, textureIndex + 2); + } + } + } + + bool hk_TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land); + decltype(&hk_TESObjectLAND_SetupMaterial) ptr_TESObjectLAND_SetupMaterial; + + bool hk_TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land) + { + auto* state = State::GetSingleton(); + + bool isPbr = false; + if (land->loadedData != nullptr) { + for (uint32_t quadIndex = 0; quadIndex < 4; ++quadIndex) { + if (land->loadedData->defQuadTextures[quadIndex] != nullptr) { + if (state->IsPBRTextureSet(land->loadedData->defQuadTextures[quadIndex]->textureSet)) { + isPbr = true; + break; + } + } + for (uint32_t textureIndex = 0; textureIndex < 6; ++textureIndex) { + if (land->loadedData->quadTextures[quadIndex][textureIndex] != nullptr) { + if (state->IsPBRTextureSet(land->loadedData->quadTextures[quadIndex][textureIndex]->textureSet)) { + isPbr = true; + break; + } + } + } + } + } + + if (!isPbr) { + return ptr_TESObjectLAND_SetupMaterial(land); + } + + static const auto settings = RE::INISettingCollection::GetSingleton(); + static const bool bEnableLandFade = settings->GetSetting("bEnableLandFade:Display"); + static const bool bDrawLandShadows = settings->GetSetting("bDrawLandShadows:Display"); + static const bool bLandSpecular = settings->GetSetting("bLandSpecular:Landscape"); + + if (land->loadedData != nullptr && land->loadedData->mesh[0] != nullptr) { + land->data.flags.set(static_cast(8)); + for (uint32_t quadIndex = 0; quadIndex < 4; ++quadIndex) { + auto shaderProperty = static_cast(RE::MemoryManager::GetSingleton()->Allocate(sizeof(RE::BSLightingShaderProperty), 0, false)); + shaderProperty->Ctor(); + + { + BSLightingShaderMaterialPBRLandscape srcMaterial; + shaderProperty->LinkMaterial(&srcMaterial, true); + } + + auto material = static_cast(shaderProperty->material); + const auto& stateData = RE::BSGraphics::State::GetSingleton()->GetRuntimeData(); + + material->diffuseTexture = stateData.defaultTextureBlack; + material->normalTexture = stateData.defaultTextureNormalMap; + material->landscapeDisplacementTextures[0] = stateData.defaultTextureBlack; + material->landscapeRMAOSTextures[0] = stateData.defaultTextureWhite; + for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles - 1; ++textureIndex) { + material->landscapeBaseColorTextures[textureIndex] = stateData.defaultTextureBlack; + material->landscapeNormalTextures[textureIndex] = stateData.defaultTextureNormalMap; + material->landscapeDisplacementTextures[textureIndex + 1] = stateData.defaultTextureBlack; + material->landscapeRMAOSTextures[textureIndex + 1] = stateData.defaultTextureWhite; + } + + if (auto defTexture = land->loadedData->defQuadTextures[quadIndex]) { + SetupLandscapeTexture(*material, *defTexture, 0); + } + for (uint32_t textureIndex = 0; textureIndex < 6; ++textureIndex) { + if (auto landTexture = land->loadedData->quadTextures[quadIndex][textureIndex]) { + SetupLandscapeTexture(*material, *landTexture, textureIndex + 1); + } + } + + if (bEnableLandFade) { + shaderProperty->unk108 = false; + } + + bool noLODLandBlend = false; + auto tes = RE::TES::GetSingleton(); + if (tes->worldSpace != nullptr) { + if (auto terrainManager = tes->worldSpace->GetTerrainManager()) { + noLODLandBlend = reinterpret_cast(terrainManager)[0x36]; + } + } + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMultiTextureLandscape, true); + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kReceiveShadows, true); + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kCastShadows, bDrawLandShadows); + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kNoLODLandBlend, noLODLandBlend); + + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kVertexLighting, true); + + const auto& children = land->loadedData->mesh[quadIndex]->GetChildren(); + auto geometry = children.empty() ? nullptr : static_cast(children[0].get()); + shaderProperty->SetupGeometry(geometry); + if (geometry != nullptr) { + geometry->GetGeometryRuntimeData().properties[1] = RE::NiPointer(shaderProperty); + } + + RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]->AttachObject(geometry); + } + + return true; + } + + return false; + } + + struct TESForm_GetFormEditorID + { + static const char* thunk(const RE::TESForm* form) + { + auto* state = State::GetSingleton(); + auto it = state->editorIDs.find(form->GetFormID()); + if (it == state->editorIDs.cend()) { + return ""; + } + return it->second.c_str(); + } + static inline REL::Relocation func; + }; + + struct TESForm_SetFormEditorID + { + static bool thunk(RE::TESForm* form, const char* editorId) + { + auto* state = State::GetSingleton(); + state->editorIDs[form->GetFormID()] = editorId; + return true; + } + static inline REL::Relocation func; + }; + struct CreateRenderTarget_Normals { static void thunk(RE::BSGraphics::Renderer* This, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) @@ -375,20 +1173,22 @@ namespace Hooks static void thunk(RE::BSGraphics::Renderer* This, RE::BSGraphics::VertexShader* a_vertexShader) { func(This, a_vertexShader); // TODO: Remove original call - auto& shaderCache = SIE::ShaderCache::Instance(); - if (shaderCache.IsEnabled()) { - auto state = State::GetSingleton(); - auto currentShader = state->currentShader; - auto type = currentShader->shaderType.get(); - if (type > 0 && type < RE::BSShader::Type::Total) { - if (state->enabledClasses[type - 1]) { - RE::BSGraphics::VertexShader* vertexShader = shaderCache.GetVertexShader(*currentShader, state->modifiedVertexDescriptor); - if (vertexShader) { - state->context->VSSetShader(reinterpret_cast(vertexShader->shader), NULL, NULL); - auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); - GET_INSTANCE_MEMBER(currentVertexShader, shadowState) - currentVertexShader = a_vertexShader; - return; + auto state = State::GetSingleton(); + if (!state->settingCustomShader) { + auto& shaderCache = SIE::ShaderCache::Instance(); + if (shaderCache.IsEnabled()) { + auto currentShader = state->currentShader; + auto type = currentShader->shaderType.get(); + if (type > 0 && type < RE::BSShader::Type::Total) { + if (state->enabledClasses[type - 1]) { + RE::BSGraphics::VertexShader* vertexShader = shaderCache.GetVertexShader(*currentShader, state->modifiedVertexDescriptor); + if (vertexShader) { + state->context->VSSetShader(reinterpret_cast(vertexShader->shader), NULL, NULL); + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + GET_INSTANCE_MEMBER(currentVertexShader, shadowState) + currentVertexShader = a_vertexShader; + return; + } } } } @@ -401,27 +1201,29 @@ namespace Hooks { static void thunk(RE::BSGraphics::Renderer* This, RE::BSGraphics::PixelShader* a_pixelShader) { - auto& shaderCache = SIE::ShaderCache::Instance(); - if (shaderCache.IsEnabled()) { - auto state = State::GetSingleton(); - auto currentShader = state->currentShader; - auto type = currentShader->shaderType.get(); - if (type > 0 && type < RE::BSShader::Type::Total) { - if (state->enabledClasses[type - 1]) { - if (state->lastModifiedPixelDescriptor != state->modifiedPixelDescriptor) { - RE::BSGraphics::PixelShader* pixelShader = shaderCache.GetPixelShader(*currentShader, state->modifiedPixelDescriptor); - if (pixelShader) { - state->context->PSSetShader(reinterpret_cast(pixelShader->shader), NULL, NULL); + auto state = State::GetSingleton(); + if (!state->settingCustomShader) { + auto& shaderCache = SIE::ShaderCache::Instance(); + if (shaderCache.IsEnabled()) { + auto currentShader = state->currentShader; + auto type = currentShader->shaderType.get(); + if (type > 0 && type < RE::BSShader::Type::Total) { + if (state->enabledClasses[type - 1]) { + if (state->lastModifiedPixelDescriptor != state->modifiedPixelDescriptor) { + RE::BSGraphics::PixelShader* pixelShader = shaderCache.GetPixelShader(*currentShader, state->modifiedPixelDescriptor); + if (pixelShader) { + state->context->PSSetShader(reinterpret_cast(pixelShader->shader), NULL, NULL); + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + GET_INSTANCE_MEMBER(currentPixelShader, shadowState) + currentPixelShader = a_pixelShader; + return; + } + } else { auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); GET_INSTANCE_MEMBER(currentPixelShader, shadowState) currentPixelShader = a_pixelShader; return; } - } else { - auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); - GET_INSTANCE_MEMBER(currentPixelShader, shadowState) - currentPixelShader = a_pixelShader; - return; } } } @@ -453,6 +1255,299 @@ namespace Hooks static inline REL::Relocation func; }; + void hk_SetPerFrameBuffers(void* renderer); + decltype(&hk_SetPerFrameBuffers) ptr_SetPerFrameBuffers; + + void hk_SetPerFrameBuffers(void* renderer) + { + ptr_SetPerFrameBuffers(renderer); + State::GetSingleton()->SetupFrame(); + } + + void hk_BSTempEffectSimpleDecal_SetupGeometry(RE::BSTempEffectSimpleDecal* decal, RE::BSGeometry* geometry, RE::BGSTextureSet* textureSet, bool blended); + decltype(&hk_BSTempEffectSimpleDecal_SetupGeometry) ptr_BSTempEffectSimpleDecal_SetupGeometry; + + void hk_BSTempEffectSimpleDecal_SetupGeometry(RE::BSTempEffectSimpleDecal* decal, RE::BSGeometry* geometry, RE::BGSTextureSet* textureSet, bool blended) + { + ptr_BSTempEffectSimpleDecal_SetupGeometry(decal, geometry, textureSet, blended); + + if (auto* shaderProperty = netimmerse_cast(geometry->GetGeometryRuntimeData().properties[1].get()); + shaderProperty != nullptr && State::GetSingleton()->IsPBRTextureSet(textureSet)) { + { + BSLightingShaderMaterialPBR srcMaterial; + shaderProperty->LinkMaterial(&srcMaterial, true); + } + + auto pbrMaterial = static_cast(shaderProperty->material); + pbrMaterial->OnLoadTextureSet(0, textureSet); + + constexpr static RE::NiColor whiteColor(1.f, 1.f, 1.f); + *shaderProperty->emissiveColor = whiteColor; + const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + shaderProperty->emissiveMult = hasEmissive ? 1.f : 0.f; + + { + using enum RE::BSShaderProperty::EShaderPropertyFlag8; + shaderProperty->SetFlags(kParallaxOcclusion, false); + shaderProperty->SetFlags(kParallax, false); + shaderProperty->SetFlags(kGlowMap, false); + shaderProperty->SetFlags(kEnvMap, false); + shaderProperty->SetFlags(kSpecular, false); + + shaderProperty->SetFlags(kVertexLighting, true); + } + } + } + + struct BSTempEffectGeometryDecal_Initialize + { + static void thunk(RE::BSTempEffectGeometryDecal* decal) + { + func(decal); + + if (decal->decal != nullptr && State::GetSingleton()->IsPBRTextureSet(decal->texSet)) { + auto shaderProperty = static_cast(RE::MemoryManager::GetSingleton()->Allocate(sizeof(RE::BSLightingShaderProperty), 0, false)); + shaderProperty->Ctor(); + + { + BSLightingShaderMaterialPBR srcMaterial; + shaderProperty->LinkMaterial(&srcMaterial, true); + } + + auto pbrMaterial = static_cast(shaderProperty->material); + pbrMaterial->OnLoadTextureSet(0, decal->texSet); + + constexpr static RE::NiColor whiteColor(1.f, 1.f, 1.f); + *shaderProperty->emissiveColor = whiteColor; + const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + shaderProperty->emissiveMult = hasEmissive ? 1.f : 0.f; + + { + using enum RE::BSShaderProperty::EShaderPropertyFlag8; + + shaderProperty->SetFlags(kSkinned, true); + shaderProperty->SetFlags(kDynamicDecal, true); + shaderProperty->SetFlags(kZBufferTest, true); + shaderProperty->SetFlags(kZBufferWrite, false); + + shaderProperty->SetFlags(kVertexLighting, true); + } + + if (auto* alphaProperty = static_cast(decal->decal->GetGeometryRuntimeData().properties[0].get())) { + alphaProperty->alphaFlags = (alphaProperty->alphaFlags & ~0x1FE) | 0xED; + } + + shaderProperty->SetupGeometry(decal->decal.get()); + decal->decal->GetGeometryRuntimeData().properties[1] = RE::NiPointer(shaderProperty); + } + } + static inline REL::Relocation func; + }; + + struct BSGrassShaderProperty_ctor + { + static RE::BSLightingShaderProperty* thunk(RE::BSLightingShaderProperty* property) + { + const uint64_t stackPointer = reinterpret_cast(_AddressOfReturnAddress()); + const uint64_t lightingPropertyAddress = stackPointer + (REL::Module::IsAE() ? 0x68 : 0x70); + auto* lightingProperty = *reinterpret_cast(lightingPropertyAddress); + + RE::BSLightingShaderProperty* grassProperty = func(property); + + if (lightingProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) { + if (auto* pbrSrcMaterial = static_cast(lightingProperty->material)) { + BSLightingShaderMaterialPBR srcMaterial; + grassProperty->LinkMaterial(&srcMaterial, true); + + grassProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMenuScreen, true); + + auto pbrMaterial = static_cast(grassProperty->material); + pbrMaterial->pbrFlags = pbrSrcMaterial->pbrFlags; + pbrMaterial->normalTexture = pbrSrcMaterial->normalTexture; + pbrMaterial->rmaosTexture = pbrSrcMaterial->rmaosTexture; + pbrMaterial->featuresTexture0 = pbrSrcMaterial->featuresTexture0; + pbrMaterial->featuresTexture1 = pbrSrcMaterial->featuresTexture1; + pbrMaterial->specularColorScale = pbrSrcMaterial->specularColorScale; + pbrMaterial->specularPower = pbrSrcMaterial->specularPower; + pbrMaterial->specularColor = pbrSrcMaterial->specularColor; + pbrMaterial->subSurfaceLightRolloff = pbrSrcMaterial->subSurfaceLightRolloff; + pbrMaterial->coatRoughness = pbrSrcMaterial->coatRoughness; + } + } + + return grassProperty; + } + static inline REL::Relocation func; + }; + + struct BSGrassShaderProperty_GetRenderPasses + { + static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator) + { + auto renderPasses = func(property, geometry, renderFlags, accumulator); + if (renderPasses == nullptr) { + return renderPasses; + } + + const bool isPbr = property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kMenuScreen); + if (isPbr) { + auto currentPass = renderPasses->head; + while (currentPass != nullptr) { + if (currentPass->shader->shaderType == RE::BSShader::Type::Grass && currentPass->passEnum != 0x5C00005C) { + currentPass->passEnum = 0x5C000042; + } + currentPass = currentPass->next; + } + } + + return renderPasses; + } + static inline REL::Relocation func; + }; + + struct BSGrassShader_SetupTechnique + { + static bool thunk(RE::BSShader* shader, uint32_t globalTechnique) + { + if (globalTechnique == 0x5C000042) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + auto* graphicsState = RE::BSGraphics::State::GetSingleton(); + auto* renderer = RE::BSGraphics::Renderer::GetSingleton(); + + const uint32_t localTechnique = static_cast(SIE::ShaderCache::GrassShaderTechniques::TruePbr); + uint32_t shaderDescriptor = localTechnique; + if (graphicsState->useEarlyZ) { + shaderDescriptor |= static_cast(SIE::ShaderCache::GrassShaderFlags::AlphaTest); + } + + const bool began = hk_BSShader_BeginTechnique(shader, shaderDescriptor, shaderDescriptor, false); + if (!began) { + return false; + } + + static auto fogMethod = REL::Relocation(REL::RelocationID(100000, 106707)); + fogMethod(); + + static auto* bShadowsOnGrass = RE::GetINISetting("bShadowsOnGrass:Display"); + if (!bShadowsOnGrass->GetBool()) { + shadowState->SetPSTexture(1, graphicsState->defaultTextureWhite->rendererTexture); + shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT); + shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kNearest); + } else { + shadowState->SetPSTexture(1, renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGET::kSHADOW_MASK]); + shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT); + + static auto* shadowMaskQuarter = RE::GetINISetting("iShadowMaskQuarter:Display"); + shadowState->SetPSTextureFilterMode(1, shadowMaskQuarter->GetSInt() != 4 ? RE::BSGraphics::TextureFilterMode::kBilinear : RE::BSGraphics::TextureFilterMode::kNearest); + } + + return true; + } + + return func(shader, globalTechnique); + }; + static inline REL::Relocation func; + }; + + struct BSGrassShader_SetupMaterial + { + static void thunk(RE::BSShader* shader, RE::BSLightingShaderMaterialBase const* material) + { + const auto& state = State::GetSingleton(); + const auto technique = static_cast(state->currentPixelDescriptor & 0b1111); + + if (technique == SIE::ShaderCache::GrassShaderTechniques::TruePbr) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + + RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + + auto* pbrMaterial = static_cast(material); + shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(0, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(2, pbrMaterial->normalTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(2, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(2, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(3, pbrMaterial->rmaosTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(3, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(3, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + stl::enumeration shaderFlags; + if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) { + shaderFlags.set(PBRShaderFlags::Subsurface); + } + + const bool hasSubsurface = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; + if (hasSubsurface) { + shadowState->SetPSTexture(4, pbrMaterial->featuresTexture0->rendererTexture); + shadowState->SetPSTextureAddressMode(4, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0); + } + + { + shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 0); + } + + { + std::array PBRParams1; + PBRParams1[0] = pbrMaterial->GetRoughnessScale(); + PBRParams1[1] = pbrMaterial->GetSpecularLevel(); + shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 1); + } + + { + std::array PBRParams2; + PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red; + PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green; + PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue; + PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity(); + shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 2); + } + + RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + } else { + func(shader, material); + } + }; + static inline REL::Relocation func; + }; + + struct TESBoundObject_Clone3D + { + static RE::NiAVObject* thunk(RE::TESBoundObject* object, RE::TESObjectREFR* ref, bool arg3) + { + auto* result = func(object, ref, arg3); + if (result != nullptr && ref != nullptr && ref->data.objectReference != nullptr && ref->data.objectReference->formType == RE::FormType::Static) { + auto* stat = static_cast(ref->data.objectReference); + if (stat->data.materialObj != nullptr && stat->data.materialObj->directionalData.singlePass) { + if (auto* pbrData = State::GetSingleton()->GetPBRMaterialObjectData(stat->data.materialObj)) { + RE::BSVisit::TraverseScenegraphGeometries(result, [pbrData](RE::BSGeometry* geometry) { + if (auto* shaderProperty = static_cast(geometry->GetGeometryRuntimeData().properties[1].get())) { + if (shaderProperty->GetMaterialType() == RE::BSShaderMaterial::Type::kLighting && + shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) { + if (auto* material = static_cast(shaderProperty->material)) { + material->projectedMaterialBaseColorScale = pbrData->baseColorScale; + material->projectedMaterialRoughness = pbrData->roughness; + material->projectedMaterialSpecularLevel = pbrData->specularLevel; + } + } + } + + return RE::BSVisit::BSVisitControl::kContinue; + }); + } + } + } + return result; + } + static inline REL::Relocation func; + }; + void Install() { SKSE::AllocTrampoline(14); @@ -492,5 +1587,51 @@ namespace Hooks stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x406, 0x409)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x1245, 0x123B, 0x1917)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0xA25, 0xA25, 0xCD2)); + + logger::info("Hooking BSLightingShaderProperty"); + stl::write_vfunc<0x18, BSLightingShaderProperty_LoadBinary>(RE::VTABLE_BSLightingShaderProperty[0]); + stl::write_vfunc<0x2A, BSLightingShaderProperty_GetRenderPasses>(RE::VTABLE_BSLightingShaderProperty[0]); + + logger::info("Hooking BSLightingShader"); + stl::write_vfunc<0x4, BSLightingShader_SetupMaterial>(RE::VTABLE_BSLightingShader[0]); + stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); + std::ignore = Detours::X64::DetourFunction(REL::RelocationID(101633, 108700).address(), (uintptr_t)&hk_BSLightingShader_GetPixelTechnique); + + logger::info("Hooking TESObjectLAND"); + *(uintptr_t*)&ptr_TESObjectLAND_SetupMaterial = Detours::X64::DetourFunction(REL::RelocationID(18368, 18791).address(), (uintptr_t)&hk_TESObjectLAND_SetupMaterial); + + logger::info("Hooking TESLandTexture"); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_TESLandTexture[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_TESLandTexture[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSTextureSet[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSTextureSet[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSMaterialObject[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSMaterialObject[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSLightingTemplate[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSLightingTemplate[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_TESWeather[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_TESWeather[0]); + + logger::info("Hooking SetPerFrameBuffers"); + *(uintptr_t*)&ptr_SetPerFrameBuffers = Detours::X64::DetourFunction(REL::RelocationID(75570, 77371).address(), (uintptr_t)&hk_SetPerFrameBuffers); + + logger::info("Hooking BSTempEffectSimpleDecal"); + *(uintptr_t*)&ptr_BSTempEffectSimpleDecal_SetupGeometry = Detours::X64::DetourFunction(REL::RelocationID(29253, 30108).address(), (uintptr_t)&hk_BSTempEffectSimpleDecal_SetupGeometry); + + logger::info("Hooking BSTempEffectGeometryDecal"); + stl::write_vfunc<0x25, BSTempEffectGeometryDecal_Initialize>(RE::VTABLE_BSTempEffectGeometryDecal[0]); + + logger::info("Hooking BSGrassShaderProperty::ctor"); + stl::write_thunk_call(REL::RelocationID(15214, 15383).address() + REL::Relocate(0x45B, 0x4F5)); + + logger::info("Hooking BSGrassShaderProperty"); + stl::write_vfunc<0x2A, BSGrassShaderProperty_GetRenderPasses>(RE::VTABLE_BSGrassShaderProperty[0]); + + logger::info("Hooking BSGrassShader"); + stl::write_vfunc<0x2, BSGrassShader_SetupTechnique>(RE::VTABLE_BSGrassShader[0]); + stl::write_vfunc<0x4, BSGrassShader_SetupMaterial>(RE::VTABLE_BSGrassShader[0]); + + logger::info("Hooking TESObjectSTAT"); + stl::write_vfunc<0x4A, TESBoundObject_Clone3D>(RE::VTABLE_TESObjectSTAT[0]); } } \ No newline at end of file diff --git a/src/Menu.cpp b/src/Menu.cpp index 79b476212..946e17a16 100644 --- a/src/Menu.cpp +++ b/src/Menu.cpp @@ -408,6 +408,64 @@ void Menu::DrawSettings() ImGui::TreePop(); } ImGui::Checkbox("Extended Frame Annotations", &State::GetSingleton()->extendedFrameAnnotations); + if (ImGui::TreeNodeEx("PBR", ImGuiTreeNodeFlags_DefaultOpen)) { + auto* state = State::GetSingleton(); + + if (const auto* player = RE::PlayerCharacter::GetSingleton()) { + if (const auto* currentCell = player->GetParentCell()) { + if (currentCell->IsInteriorCell()) { + if (const auto* lightingTemplate = currentCell->GetRuntimeData().lightingTemplate) { + if (ImGui::TreeNodeEx("Lighting Template Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + const auto* editorId = lightingTemplate->GetFormEditorID(); + ImGui::Text(std::format("Current Lighting Template : {}", editorId).c_str()); + + auto& pbrData = state->pbrLightingTemplates[editorId]; + + ImGui::SliderFloat("Directional Light Scale", &pbrData.directionalLightColorScale, 0.f, 5.f); + ImGui::SliderFloat("Directional Ambient Light Scale", &pbrData.directionalAmbientLightColorScale, 0.f, 5.f); + + if (ImGui::Button("Save")) { + state->SavePBRLightingTemplateData(editorId); + } + + ImGui::TreePop(); + } + } + } else if (RE::Sky* sky = RE::Sky::GetSingleton()) { + if (const auto* weather = sky->currentWeather) { + if (ImGui::TreeNodeEx("Weather Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + const auto* editorId = weather->GetFormEditorID(); + ImGui::Text(std::format("Current Weather : {}", editorId).c_str()); + + auto& pbrData = state->pbrWeathers[editorId]; + + ImGui::SliderFloat("Directional Light Scale", &pbrData.directionalLightColorScale, 0.f, 5.f); + ImGui::SliderFloat("Directional Ambient Light Scale", &pbrData.directionalAmbientLightColorScale, 0.f, 5.f); + + if (ImGui::Button("Save")) { + state->SavePBRWeatherData(editorId); + } + + ImGui::TreePop(); + } + } + } + } + } + + bool useMultipleScattering = state->pbrSettings.useMultipleScattering; + bool useMultiBounceAO = state->pbrSettings.useMultiBounceAO; + if (ImGui::Checkbox("Use Multiple Scattering", &useMultipleScattering)) { + state->pbrSettings.useMultipleScattering = useMultipleScattering; + } + if (ImGui::Checkbox("Use Multi-bounce AO", &useMultiBounceAO)) { + state->pbrSettings.useMultiBounceAO = useMultiBounceAO; + } + + ImGui::SliderFloat("Direct Light Color Multiplier", &state->globalPBRDirectLightColorMultiplier, 1e-3f, 1e2f, "%.3f", ImGuiSliderFlags_Logarithmic); + ImGui::SliderFloat("Ambient Light Color Multiplier", &state->globalPBRAmbientLightColorMultiplier, 1e-3f, 1e2f, "%.3f", ImGuiSliderFlags_Logarithmic); + ImGui::TreePop(); + } } if (ImGui::CollapsingHeader("Replace Original Shaders", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) { diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp index f5fe830fd..7dbd5067b 100644 --- a/src/ShaderCache.cpp +++ b/src/ShaderCache.cpp @@ -58,17 +58,14 @@ namespace SIE static REL::Relocation VanillaGetLightingShaderDefines( RELOCATION_ID(101631, 108698)); - const auto technique = - static_cast(GetTechnique(descriptor)); - int lastIndex = 0; - if (technique == ShaderCache::LightingShaderTechniques::Outline) - defines[lastIndex++] = { "OUTLINE", nullptr }; - if (descriptor & static_cast(ShaderCache::LightingShaderFlags::Deferred)) { defines[lastIndex++] = { "DEFERRED", nullptr }; } + if ((descriptor & static_cast(ShaderCache::LightingShaderFlags::TruePbr)) != 0) { + defines[lastIndex++] = { "TRUE_PBR", nullptr }; + } for (auto* feature : Feature::GetFeatureList()) { if (feature->loaded && feature->HasShaderDefine(RE::BSShader::Type::Lighting)) { @@ -203,6 +200,8 @@ namespace SIE int lastIndex = 0; if (technique == static_cast(ShaderCache::GrassShaderTechniques::RenderDepth)) { defines[lastIndex++] = { "RENDER_DEPTH", nullptr }; + } else if (technique == static_cast(ShaderCache::GrassShaderTechniques::TruePbr)) { + defines[lastIndex++] = { "TRUE_PBR", nullptr }; } if (descriptor & static_cast(ShaderCache::GrassShaderFlags::AlphaTest)) { defines[lastIndex++] = { "DO_ALPHA_TEST", nullptr }; @@ -728,6 +727,15 @@ namespace SIE { "SSRParams", 16 }, { "WorldMapOverlayParametersPS", 17 }, { "AmbientColor", 18 }, + + { "PBRFlags", 36 }, + { "PBRParams1", 37 }, + { "LandscapeTexture2PBRParams", 38 }, + { "LandscapeTexture3PBRParams", 39 }, + { "LandscapeTexture4PBRParams", 40 }, + { "LandscapeTexture5PBRParams", 41 }, + { "LandscapeTexture6PBRParams", 42 }, + { "PBRParams2", 43 }, }; auto& bloodSplatterVS = result[static_cast(RE::BSShader::Type::BloodSplatter)] @@ -798,6 +806,14 @@ namespace SIE { "ShadowClampValue", 14 }, }; + auto& grassPS = result[static_cast(RE::BSShader::Type::Grass)] + [static_cast(ShaderClass::Pixel)]; + grassPS = { + { "PBRFlags", 0 }, + { "PBRParams1", 1 }, + { "PBRParams2", 2 }, + }; + auto& particleVS = result[static_cast(RE::BSShader::Type::Particle)] [static_cast(ShaderClass::Vertex)]; particleVS = { @@ -1012,10 +1028,6 @@ namespace SIE return; } - if (desc.ConstantBuffers <= 0) { - return; - } - if (shaderClass == ShaderClass::Vertex) { vertexDesc = 0b1111; bool hasTexcoord2 = false; @@ -1067,6 +1079,10 @@ namespace SIE } } + if (desc.ConstantBuffers <= 0) { + return; + } + auto mapBufferConsts = [&](const char* bufferName, size_t& bufferSize) { auto bufferReflector = reflector.GetConstantBufferByName(bufferName); @@ -1256,7 +1272,6 @@ namespace SIE ID3DBlob* strippedShaderBlob = nullptr; const uint32_t stripFlags = D3DCOMPILER_STRIP_DEBUG_INFO | - D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_TEST_BLOBS | D3DCOMPILER_STRIP_PRIVATE_DATA; diff --git a/src/ShaderCache.h b/src/ShaderCache.h index 605d638f4..39135de26 100644 --- a/src/ShaderCache.h +++ b/src/ShaderCache.h @@ -226,7 +226,6 @@ namespace SIE Cloud = 17, // unused LODLandNoise = 18, MTLandLODBlend = 19, - Outline = 20, }; enum class LightingShaderFlags @@ -236,6 +235,7 @@ namespace SIE ModelSpaceNormals = 1 << 2, // flags 3 to 8 are unused by vanilla // Community Shaders start + TruePbr = 1 << 3, Deferred = 1 << 4, // Community Shaders end Specular = 1 << 9, @@ -289,6 +289,7 @@ namespace SIE enum class GrassShaderTechniques { RenderDepth = 8, + TruePbr = 9, }; enum class GrassShaderFlags diff --git a/src/State.cpp b/src/State.cpp index 2039169c2..8ba29d1a3 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -15,6 +15,84 @@ #include "VariableRateShading.h" +namespace PNState +{ + template + bool Read(const json& config, ResultType& result) + { + if constexpr (std::is_same_v> || std::is_same_v) { + if (config.is_array() && config.size() == 3 && + config[0].is_number_float() && config[1].is_number_float() && + config[2].is_number_float()) { + result[0] = config[0]; + result[1] = config[1]; + result[2] = config[2]; + return true; + } + } + if constexpr (std::is_same_v) { + if (config.is_number_float()) { + result = config; + return true; + } + } + return false; + } + + void ReadPBRRecordConfigs(const std::string& rootPath, std::function recordReader) + { + if (std::filesystem::exists(rootPath)) { + auto configs = clib_util::distribution::get_configs(rootPath, "", ".json"); + + if (configs.empty()) { + logger::warn("[TruePBR] no .json files were found within the {} folder, aborting...", rootPath); + return; + } + + logger::info("[TruePBR] {} matching jsons found", configs.size()); + + for (auto& path : configs) { + logger::info("[TruePBR] loading json : {}", path); + + std::ifstream fileStream(path); + if (!fileStream.is_open()) { + logger::error("[TruePBR] failed to read {}", path); + continue; + } + + json config; + try { + fileStream >> config; + } catch (const nlohmann::json::parse_error& e) { + logger::error("[TruePBR] failed to parse {} : {}", path, e.what()); + continue; + } + + const auto editorId = std::filesystem::path(path).stem().string(); + recordReader(editorId, config); + } + } + } + + void SavePBRRecordConfig(const std::string& rootPath, const std::string& editorId, const json& config) + { + std::filesystem::create_directory(rootPath); + + const std::string outputPath = std::format("{}\\{}.json", rootPath, editorId); + std::ofstream fileStream(outputPath); + if (!fileStream.is_open()) { + logger::error("[TruePBR] failed to write {}", outputPath); + return; + } + try { + fileStream << std::setw(4) << config; + } catch (const nlohmann::json::type_error& e) { + logger::error("[TruePBR] failed to serialize {} : {}", outputPath, e.what()); + return; + } + } +} + void State::Draw() { const auto& shaderCache = SIE::ShaderCache::Instance(); @@ -86,6 +164,10 @@ void State::Reset() void State::Setup() { + SetupTextureSetData(); + SetupMaterialObjectData(); + SetupLightingTemplateData(); + SetupWeatherData(); SetupResources(); for (auto* feature : Feature::GetFeatureList()) if (feature->loaded) @@ -191,6 +273,24 @@ void State::Load(ConfigMode a_configMode) } } + if (settings["PBR"].is_object()) { + json& pbr = settings["PBR"]; + + if (pbr["Use Multiple Scattering"].is_boolean()) { + pbrSettings.useMultipleScattering = pbr["Use Multiple Scattering"]; + } + if (pbr["Use Multi-bounce AO"].is_boolean()) { + pbrSettings.useMultiBounceAO = pbr["Use Multi-bounce AO"]; + } + + if (pbr["Direct Light Color Multiplier"].is_number_float()) { + globalPBRDirectLightColorMultiplier = pbr["Direct Light Color Multiplier"]; + } + if (pbr["Ambient Light Color Multiplier"].is_number_float()) { + globalPBRAmbientLightColorMultiplier = pbr["Ambient Light Color Multiplier"]; + } + } + for (auto* feature : Feature::GetFeatureList()) feature->Load(settings); i.close(); @@ -226,6 +326,16 @@ void State::Save(ConfigMode a_configMode) settings["General"] = general; + { + json pbr; + pbr["Use Multiple Scattering"] = pbrSettings.useMultipleScattering; + pbr["Use Multi-bounce AO"] = pbrSettings.useMultiBounceAO; + + pbr["Direct Light Color Multiplier"] = globalPBRDirectLightColorMultiplier; + pbr["Ambient Light Color Multiplier"] = globalPBRAmbientLightColorMultiplier; + settings["PBR"] = pbr; + } + json originalShaders; for (int classIndex = 0; classIndex < RE::BSShader::Type::Total - 1; ++classIndex) { originalShaders[magic_enum::enum_name((RE::BSShader::Type)(classIndex + 1))] = enabledClasses[classIndex]; @@ -520,3 +630,207 @@ void State::UpdateSharedData() context->PSSetShaderResources(20, 1, &srv); } + +void State::SetupFrame() +{ + float newDirectionalLightScale = 1.f; + float newDirectionalAmbientLightScale = 1.f; + + if (const auto* player = RE::PlayerCharacter::GetSingleton()) { + if (const auto* currentCell = player->GetParentCell()) { + if (currentCell->IsInteriorCell()) { + if (const auto* lightingTemplate = currentCell->GetRuntimeData().lightingTemplate) { + const auto* editorId = lightingTemplate->GetFormEditorID(); + if (auto it = pbrLightingTemplates.find(editorId); it != pbrLightingTemplates.cend()) { + newDirectionalLightScale = it->second.directionalLightColorScale; + newDirectionalAmbientLightScale = it->second.directionalAmbientLightColorScale; + } + } + } else if (RE::Sky* sky = RE::Sky::GetSingleton()) { + if (const auto* weather = sky->currentWeather) { + const auto* editorId = weather->GetFormEditorID(); + if (auto it = pbrWeathers.find(editorId); it != pbrWeathers.cend()) { + newDirectionalLightScale = it->second.directionalLightColorScale; + newDirectionalAmbientLightScale = it->second.directionalAmbientLightColorScale; + } + } + } + } + } + + weatherPBRDirectionalLightColorMultiplier = newDirectionalLightScale; + weatherPBRDirectionalAmbientLightColorMultiplier = newDirectionalAmbientLightScale; + + pbrSettings.directionalLightColorMultiplier = globalPBRDirectLightColorMultiplier * weatherPBRDirectionalLightColorMultiplier; + pbrSettings.pointLightColorMultiplier = globalPBRDirectLightColorMultiplier; + pbrSettings.ambientLightColorMultiplier = globalPBRAmbientLightColorMultiplier * weatherPBRDirectionalAmbientLightColorMultiplier; +} + +void State::SetupTextureSetData() +{ + logger::info("[TruePBR] loading PBR texture set configs"); + + pbrTextureSets.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRTextureSets", [this](const std::string& editorId, const json& config) { + PBRTextureSetData textureSetData; + + PNState::Read(config["roughnessScale"], textureSetData.roughnessScale); + PNState::Read(config["displacementScale"], textureSetData.displacementScale); + PNState::Read(config["specularLevel"], textureSetData.specularLevel); + PNState::Read(config["subsurfaceColor"], textureSetData.subsurfaceColor); + PNState::Read(config["subsurfaceOpacity"], textureSetData.subsurfaceOpacity); + PNState::Read(config["coatColor"], textureSetData.coatColor); + PNState::Read(config["coatStrength"], textureSetData.coatStrength); + PNState::Read(config["coatRoughness"], textureSetData.coatRoughness); + PNState::Read(config["coatSpecularLevel"], textureSetData.coatSpecularLevel); + PNState::Read(config["innerLayerDisplacementOffset"], textureSetData.innerLayerDisplacementOffset); + PNState::Read(config["fuzzColor"], textureSetData.fuzzColor); + PNState::Read(config["fuzzWeight"], textureSetData.fuzzWeight); + + pbrTextureSets.insert_or_assign(editorId, textureSetData); + }); +} + +State::PBRTextureSetData* State::GetPBRTextureSetData(const RE::TESForm* textureSet) +{ + if (textureSet == nullptr) { + return nullptr; + } + + auto it = pbrTextureSets.find(textureSet->GetFormEditorID()); + if (it == pbrTextureSets.end()) { + return nullptr; + } + return &it->second; +} + +bool State::IsPBRTextureSet(const RE::TESForm* textureSet) +{ + return GetPBRTextureSetData(textureSet) != nullptr; +} + +void State::SetupMaterialObjectData() +{ + logger::info("[TruePBR] loading PBR material object configs"); + + pbrMaterialObjects.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRMaterialObjects", [this](const std::string& editorId, const json& config) { + PBRMaterialObjectData materialObjectData; + + PNState::Read(config["baseColorScale"], materialObjectData.baseColorScale); + PNState::Read(config["roughness"], materialObjectData.roughness); + PNState::Read(config["specularLevel"], materialObjectData.specularLevel); + + pbrMaterialObjects.insert_or_assign(editorId, materialObjectData); + }); +} + +State::PBRMaterialObjectData* State::GetPBRMaterialObjectData(const RE::TESForm* materialObject) +{ + if (materialObject == nullptr) { + return nullptr; + } + + auto it = pbrMaterialObjects.find(materialObject->GetFormEditorID()); + if (it == pbrMaterialObjects.end()) { + return nullptr; + } + return &it->second; +} + +bool State::IsPBRMaterialObject(const RE::TESForm* materialObject) +{ + return GetPBRMaterialObjectData(materialObject) != nullptr; +} + +void State::SetupLightingTemplateData() +{ + logger::info("[TruePBR] loading PBR lighting template configs"); + + pbrLightingTemplates.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRLightingTemplates", [this](const std::string& editorId, const json& config) { + PBRLightingTemplateData lightingTemplateData; + + PNState::Read(config["directionalLightColorScale"], lightingTemplateData.directionalLightColorScale); + PNState::Read(config["directionalAmbientLightColorScale"], lightingTemplateData.directionalAmbientLightColorScale); + + pbrLightingTemplates.insert_or_assign(editorId, lightingTemplateData); + }); +} + +State::PBRLightingTemplateData* State::GetPBRLightingTemplateData(const RE::TESForm* lightingTemplate) +{ + if (lightingTemplate == nullptr) { + return nullptr; + } + + auto it = pbrLightingTemplates.find(lightingTemplate->GetFormEditorID()); + if (it == pbrLightingTemplates.end()) { + return nullptr; + } + return &it->second; +} + +bool State::IsPBRLightingTemplate(const RE::TESForm* lightingTemplate) +{ + return GetPBRLightingTemplateData(lightingTemplate) != nullptr; +} + +void State::SavePBRLightingTemplateData(const std::string& editorId) +{ + const auto& pbrLightingTemplateData = pbrLightingTemplates[editorId]; + + json config; + config["directionalLightColorScale"] = pbrLightingTemplateData.directionalLightColorScale; + config["directionalAmbientLightColorScale"] = pbrLightingTemplateData.directionalAmbientLightColorScale; + + PNState::SavePBRRecordConfig("Data\\PBRLightingTemplates\\", editorId, config); +} + +void State::SetupWeatherData() +{ + logger::info("[TruePBR] loading PBR weather configs"); + + pbrWeathers.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRWeathers", [this](const std::string& editorId, const json& config) { + PBRWeatherData weatherData; + + PNState::Read(config["directionalLightColorScale"], weatherData.directionalLightColorScale); + PNState::Read(config["directionalAmbientLightColorScale"], weatherData.directionalAmbientLightColorScale); + + pbrWeathers.insert_or_assign(editorId, weatherData); + }); +} + +State::PBRWeatherData* State::GetPBRWeatherData(const RE::TESForm* weather) +{ + if (weather == nullptr) { + return nullptr; + } + + auto it = pbrWeathers.find(weather->GetFormEditorID()); + if (it == pbrWeathers.end()) { + return nullptr; + } + return &it->second; +} + +bool State::IsPBRWeather(const RE::TESForm* weather) +{ + return GetPBRWeatherData(weather) != nullptr; +} + +void State::SavePBRWeatherData(const std::string& editorId) +{ + const auto& pbrWeatherData = pbrWeathers[editorId]; + + json config; + config["directionalLightColorScale"] = pbrWeatherData.directionalLightColorScale; + config["directionalAmbientLightColorScale"] = pbrWeatherData.directionalAmbientLightColorScale; + + PNState::SavePBRRecordConfig("Data\\PBRWeathers\\", editorId, config); +} diff --git a/src/State.h b/src/State.h index a31cb396a..3743060eb 100644 --- a/src/State.h +++ b/src/State.h @@ -20,6 +20,7 @@ class State bool enableVShaders = true; bool updateShader = true; + bool settingCustomShader = false; RE::BSShader* currentShader = nullptr; uint32_t currentVertexDescriptor = 0; @@ -45,6 +46,7 @@ class State void Draw(); void Reset(); void Setup(); + void SetupFrame(); void Load(ConfigMode a_configMode = ConfigMode::USER); void Save(ConfigMode a_configMode = ConfigMode::USER); @@ -138,6 +140,90 @@ class State ID3D11DeviceContext* context = nullptr; ID3D11Device* device = nullptr; + std::unordered_map editorIDs; + + float globalPBRDirectLightColorMultiplier = 1.f; + float globalPBRAmbientLightColorMultiplier = 1.f; + + float weatherPBRDirectionalLightColorMultiplier = 1.f; + float weatherPBRDirectionalAmbientLightColorMultiplier = 1.f; + +#pragma warning(push) +#pragma warning(disable: 4324) + struct alignas(16) PBRSettings + { + float directionalLightColorMultiplier = 1.f; + float pointLightColorMultiplier = 1.f; + float ambientLightColorMultiplier = 1.f; + uint32_t useMultipleScattering = true; + uint32_t useMultiBounceAO = true; + } pbrSettings{}; +#pragma warning(pop) + + struct PBRTextureSetData + { + float roughnessScale = 1.f; + float displacementScale = 1.f; + float specularLevel = 0.04f; + + RE::NiColor subsurfaceColor; + float subsurfaceOpacity = 0.f; + + RE::NiColor coatColor = { 1.f, 1.f, 1.f }; + float coatStrength = 1.f; + float coatRoughness = 1.f; + float coatSpecularLevel = 0.04f; + float innerLayerDisplacementOffset = 0.f; + + RE::NiColor fuzzColor; + float fuzzWeight = 0.f; + }; + + void SetupTextureSetData(); + State::PBRTextureSetData* GetPBRTextureSetData(const RE::TESForm* textureSet); + bool IsPBRTextureSet(const RE::TESForm* textureSet); + + std::unordered_map pbrTextureSets; + + struct PBRMaterialObjectData + { + std::array baseColorScale = { 1.f, 1.f, 1.f }; + float roughness = 1.f; + float specularLevel = 1.f; + }; + + void SetupMaterialObjectData(); + State::PBRMaterialObjectData* GetPBRMaterialObjectData(const RE::TESForm* materialObject); + bool IsPBRMaterialObject(const RE::TESForm* materialObject); + + std::unordered_map pbrMaterialObjects; + + struct PBRLightingTemplateData + { + float directionalLightColorScale = 1.f; + float directionalAmbientLightColorScale = 1.f; + }; + + void SetupLightingTemplateData(); + State::PBRLightingTemplateData* GetPBRLightingTemplateData(const RE::TESForm* lightingTemplate); + bool IsPBRLightingTemplate(const RE::TESForm* lightingTemplate); + void SavePBRLightingTemplateData(const std::string& editorId); + + std::unordered_map pbrLightingTemplates; + + struct PBRWeatherData + { + float directionalLightColorScale = 1.f; + float directionalAmbientLightColorScale = 1.f; + }; + + void SetupWeatherData(); + State::PBRWeatherData* GetPBRWeatherData(const RE::TESForm* weather); + bool IsPBRWeather(const RE::TESForm* weather); + void SavePBRWeatherData(const std::string& editorId); + + std::unordered_map pbrWeathers; + private: std::shared_ptr pPerf; bool initialized = false;