diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 5c72ae3f2a6..17598262317 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -97,7 +97,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Added support for alpha to coverage for HDRP shaders and shader graph - Added support for Quality Levels to Subsurface Scattering. - Added option to disable XR rendering on the camera settings. -- Added a frame setting for alpha to mask. +- Added support for specular AA from geometric curvature in AxF +- Added support for baked AO (no input for now) in AxF ### Fixed - Fix when rescale probe all direction below zero (1219246) @@ -516,6 +517,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed a regression in the ray traced indirect diffuse due to the new probe system. - Fix for range compression factor for probes going negative (now clamped to positive values). - Fixed path validation when creating new volume profile (case 1229933) +- Fix reflection hierarchy for CARPAINT in AxF. +- Fix precise fresnel for delta lights for SVBRDF in AxF. ### Changed - Color buffer pyramid is not allocated anymore if neither refraction nor distortion are enabled diff --git a/com.unity.render-pipelines.high-definition/Editor/Material/AxF/AxFGUI.cs b/com.unity.render-pipelines.high-definition/Editor/Material/AxF/AxFGUI.cs index b18aceccd86..51c0ab3c4a5 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Material/AxF/AxFGUI.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Material/AxF/AxFGUI.cs @@ -26,7 +26,7 @@ class AxFGUI : ShaderGUI { new SurfaceOptionUIBlock(MaterialUIBlock.Expandable.Base, features: SurfaceOptionUIBlock.Features.Unlit | SurfaceOptionUIBlock.Features.ReceiveSSR), new AxfSurfaceInputsUIBlock(MaterialUIBlock.Expandable.Input), - new AdvancedOptionsUIBlock(MaterialUIBlock.Expandable.Advance, AdvancedOptionsUIBlock.Features.Instancing | AdvancedOptionsUIBlock.Features.AddPrecomputedVelocity), + new AdvancedOptionsUIBlock(MaterialUIBlock.Expandable.Advance, AdvancedOptionsUIBlock.Features.Instancing | AdvancedOptionsUIBlock.Features.SpecularOcclusion | AdvancedOptionsUIBlock.Features.AddPrecomputedVelocity), }; public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props) @@ -46,7 +46,9 @@ public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] pro ///////////////////////////////////////////////////////////////////////////////////////////////// // AxF material keywords - static string m_AxF_BRDFTypeText = "_AxF_BRDFType"; + const string kAxF_BRDFType = "_AxF_BRDFType"; + const string kEnableGeometricSpecularAA = "_EnableGeometricSpecularAA"; + const string kSpecularOcclusionMode = "_SpecularOcclusionMode"; // match AdvancedOptionsUIBlock.kSpecularOcclusionMode : TODO move both to HDStringConstants. // All Setup Keyword functions must be static. It allow to create script to automatically update the shaders with a script if code change static public void SetupMaterialKeywordsAndPass(Material material) @@ -54,7 +56,7 @@ static public void SetupMaterialKeywordsAndPass(Material material) material.SetupBaseUnlitKeywords(); material.SetupBaseUnlitPass(); - AxfBrdfType BRDFType = (AxfBrdfType)material.GetFloat(m_AxF_BRDFTypeText); + AxfBrdfType BRDFType = (AxfBrdfType)material.GetFloat(kAxF_BRDFType); CoreUtils.SetKeyword(material, "_AXF_BRDF_TYPE_SVBRDF", BRDFType == AxfBrdfType.SVBRDF); CoreUtils.SetKeyword(material, "_AXF_BRDF_TYPE_CAR_PAINT", BRDFType == AxfBrdfType.CAR_PAINT); @@ -65,6 +67,8 @@ static public void SetupMaterialKeywordsAndPass(Material material) CoreUtils.SetKeyword(material, "_DISABLE_DECALS", decalsEnabled == false); bool ssrEnabled = material.HasProperty(kEnableSSR) && material.GetFloat(kEnableSSR) > 0.0f; CoreUtils.SetKeyword(material, "_DISABLE_SSR", ssrEnabled == false); + CoreUtils.SetKeyword(material, "_ENABLE_GEOMETRIC_SPECULAR_AA", material.HasProperty(kEnableGeometricSpecularAA) && material.GetFloat(kEnableGeometricSpecularAA) > 0.0f); + CoreUtils.SetKeyword(material, "_SPECULAR_OCCLUSION_NONE", material.HasProperty(kSpecularOcclusionMode) && material.GetFloat(kSpecularOcclusionMode) == 0.0f); BaseLitGUI.SetupStencil(material, receivesSSR: ssrEnabled, useSplitLighting: false); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs index b0fc37dadc1..2d161eea64e 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs @@ -44,6 +44,13 @@ public enum FeatureFlags [GenerateHLSL(PackingRules.Exact, false, false, true, 1200)] public struct SurfaceData { + [MaterialSharedPropertyMapping(MaterialSharedProperty.AmbientOcclusion)] + [SurfaceDataAttributes("Ambient Occlusion")] + public float ambientOcclusion; + + [SurfaceDataAttributes("Specular Occlusion")] + public float specularOcclusion; + [MaterialSharedPropertyMapping(MaterialSharedProperty.Normal)] [SurfaceDataAttributes(new string[] {"Normal", "Normal View Space"}, true)] public Vector3 normalWS; @@ -64,7 +71,7 @@ public struct SurfaceData public Vector3 fresnelF0; [SurfaceDataAttributes("Specular Lobe")] - public Vector2 specularLobe; + public Vector3 specularLobe; // .xy for SVBRDF, .xyz for CARPAINT2, for _CarPaint2_CTSpreads per lobe roughnesses [SurfaceDataAttributes("Height")] public float height_mm; @@ -102,6 +109,9 @@ public struct SurfaceData [GenerateHLSL(PackingRules.Exact, false, false, true, 1250)] public struct BSDFData { + public float ambientOcclusion; + public float specularOcclusion; + [SurfaceDataAttributes(new string[] { "Normal WS", "Normal View Space" }, true)] public Vector3 normalWS; [SurfaceDataAttributes("", true)] @@ -113,7 +123,7 @@ public struct BSDFData public Vector3 diffuseColor; public Vector3 specularColor; public Vector3 fresnelF0; - public Vector2 roughness; + public Vector3 roughness; // .xy for SVBRDF, .xyz for CARPAINT2, for _CarPaint2_CTSpreads per lobe roughnesses public float height_mm; // Car Paint Variables diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs.hlsl index 12b8d46ab0b..7362aefc55d 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.cs.hlsl @@ -18,53 +18,59 @@ // // UnityEngine.Rendering.HighDefinition.AxF+SurfaceData: static fields // -#define DEBUGVIEW_AXF_SURFACEDATA_NORMAL (1200) -#define DEBUGVIEW_AXF_SURFACEDATA_NORMAL_VIEW_SPACE (1201) -#define DEBUGVIEW_AXF_SURFACEDATA_TANGENT (1202) -#define DEBUGVIEW_AXF_SURFACEDATA_DIFFUSE_COLOR (1203) -#define DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_COLOR (1204) -#define DEBUGVIEW_AXF_SURFACEDATA_FRESNEL_F0 (1205) -#define DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_LOBE (1206) -#define DEBUGVIEW_AXF_SURFACEDATA_HEIGHT (1207) -#define DEBUGVIEW_AXF_SURFACEDATA_ANISOTROPIC_ANGLE (1208) -#define DEBUGVIEW_AXF_SURFACEDATA_FLAKES_UV (1209) -#define DEBUGVIEW_AXF_SURFACEDATA_FLAKES_MIP (1210) -#define DEBUGVIEW_AXF_SURFACEDATA_CLEARCOAT_COLOR (1211) -#define DEBUGVIEW_AXF_SURFACEDATA_CLEARCOAT_NORMAL (1212) -#define DEBUGVIEW_AXF_SURFACEDATA_CLEARCOAT_IOR (1213) -#define DEBUGVIEW_AXF_SURFACEDATA_GEOMETRIC_NORMAL (1214) -#define DEBUGVIEW_AXF_SURFACEDATA_GEOMETRIC_NORMAL_VIEW_SPACE (1215) +#define DEBUGVIEW_AXF_SURFACEDATA_AMBIENT_OCCLUSION (1200) +#define DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_OCCLUSION (1201) +#define DEBUGVIEW_AXF_SURFACEDATA_NORMAL (1202) +#define DEBUGVIEW_AXF_SURFACEDATA_NORMAL_VIEW_SPACE (1203) +#define DEBUGVIEW_AXF_SURFACEDATA_TANGENT (1204) +#define DEBUGVIEW_AXF_SURFACEDATA_DIFFUSE_COLOR (1205) +#define DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_COLOR (1206) +#define DEBUGVIEW_AXF_SURFACEDATA_FRESNEL_F0 (1207) +#define DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_LOBE (1208) +#define DEBUGVIEW_AXF_SURFACEDATA_HEIGHT (1209) +#define DEBUGVIEW_AXF_SURFACEDATA_ANISOTROPIC_ANGLE (1210) +#define DEBUGVIEW_AXF_SURFACEDATA_FLAKES_UV (1211) +#define DEBUGVIEW_AXF_SURFACEDATA_FLAKES_MIP (1212) +#define DEBUGVIEW_AXF_SURFACEDATA_CLEARCOAT_COLOR (1213) +#define DEBUGVIEW_AXF_SURFACEDATA_CLEARCOAT_NORMAL (1214) +#define DEBUGVIEW_AXF_SURFACEDATA_CLEARCOAT_IOR (1215) +#define DEBUGVIEW_AXF_SURFACEDATA_GEOMETRIC_NORMAL (1216) +#define DEBUGVIEW_AXF_SURFACEDATA_GEOMETRIC_NORMAL_VIEW_SPACE (1217) // // UnityEngine.Rendering.HighDefinition.AxF+BSDFData: static fields // -#define DEBUGVIEW_AXF_BSDFDATA_NORMAL_WS (1250) -#define DEBUGVIEW_AXF_BSDFDATA_NORMAL_VIEW_SPACE (1251) -#define DEBUGVIEW_AXF_BSDFDATA_TANGENT_WS (1252) -#define DEBUGVIEW_AXF_BSDFDATA_BI_TANGENT_WS (1253) -#define DEBUGVIEW_AXF_BSDFDATA_DIFFUSE_COLOR (1254) -#define DEBUGVIEW_AXF_BSDFDATA_SPECULAR_COLOR (1255) -#define DEBUGVIEW_AXF_BSDFDATA_FRESNEL_F0 (1256) -#define DEBUGVIEW_AXF_BSDFDATA_ROUGHNESS (1257) -#define DEBUGVIEW_AXF_BSDFDATA_HEIGHT_MM (1258) -#define DEBUGVIEW_AXF_BSDFDATA_FLAKES_UV (1259) -#define DEBUGVIEW_AXF_BSDFDATA_FLAKES_MIP (1260) -#define DEBUGVIEW_AXF_BSDFDATA_CLEARCOAT_COLOR (1261) -#define DEBUGVIEW_AXF_BSDFDATA_CLEARCOAT_NORMAL_WS (1262) -#define DEBUGVIEW_AXF_BSDFDATA_CLEARCOAT_IOR (1263) -#define DEBUGVIEW_AXF_BSDFDATA_GEOMETRIC_NORMAL (1264) -#define DEBUGVIEW_AXF_BSDFDATA_GEOMETRIC_NORMAL_VIEW_SPACE (1265) +#define DEBUGVIEW_AXF_BSDFDATA_AMBIENT_OCCLUSION (1250) +#define DEBUGVIEW_AXF_BSDFDATA_SPECULAR_OCCLUSION (1251) +#define DEBUGVIEW_AXF_BSDFDATA_NORMAL_WS (1252) +#define DEBUGVIEW_AXF_BSDFDATA_NORMAL_VIEW_SPACE (1253) +#define DEBUGVIEW_AXF_BSDFDATA_TANGENT_WS (1254) +#define DEBUGVIEW_AXF_BSDFDATA_BI_TANGENT_WS (1255) +#define DEBUGVIEW_AXF_BSDFDATA_DIFFUSE_COLOR (1256) +#define DEBUGVIEW_AXF_BSDFDATA_SPECULAR_COLOR (1257) +#define DEBUGVIEW_AXF_BSDFDATA_FRESNEL_F0 (1258) +#define DEBUGVIEW_AXF_BSDFDATA_ROUGHNESS (1259) +#define DEBUGVIEW_AXF_BSDFDATA_HEIGHT_MM (1260) +#define DEBUGVIEW_AXF_BSDFDATA_FLAKES_UV (1261) +#define DEBUGVIEW_AXF_BSDFDATA_FLAKES_MIP (1262) +#define DEBUGVIEW_AXF_BSDFDATA_CLEARCOAT_COLOR (1263) +#define DEBUGVIEW_AXF_BSDFDATA_CLEARCOAT_NORMAL_WS (1264) +#define DEBUGVIEW_AXF_BSDFDATA_CLEARCOAT_IOR (1265) +#define DEBUGVIEW_AXF_BSDFDATA_GEOMETRIC_NORMAL (1266) +#define DEBUGVIEW_AXF_BSDFDATA_GEOMETRIC_NORMAL_VIEW_SPACE (1267) // Generated from UnityEngine.Rendering.HighDefinition.AxF+SurfaceData // PackingRules = Exact struct SurfaceData { + float ambientOcclusion; + float specularOcclusion; float3 normalWS; float3 tangentWS; float3 diffuseColor; float3 specularColor; float3 fresnelF0; - float2 specularLobe; + float3 specularLobe; float height_mm; float anisotropyAngle; float2 flakesUV; @@ -79,13 +85,15 @@ struct SurfaceData // PackingRules = Exact struct BSDFData { + float ambientOcclusion; + float specularOcclusion; float3 normalWS; float3 tangentWS; float3 biTangentWS; float3 diffuseColor; float3 specularColor; float3 fresnelF0; - float2 roughness; + float3 roughness; float height_mm; float2 flakesUV; float flakesMipLevel; @@ -102,6 +110,12 @@ void GetGeneratedSurfaceDataDebug(uint paramId, SurfaceData surfacedata, inout f { switch (paramId) { + case DEBUGVIEW_AXF_SURFACEDATA_AMBIENT_OCCLUSION: + result = surfacedata.ambientOcclusion.xxx; + break; + case DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_OCCLUSION: + result = surfacedata.specularOcclusion.xxx; + break; case DEBUGVIEW_AXF_SURFACEDATA_NORMAL: result = surfacedata.normalWS * 0.5 + 0.5; break; @@ -123,7 +137,7 @@ void GetGeneratedSurfaceDataDebug(uint paramId, SurfaceData surfacedata, inout f result = surfacedata.fresnelF0; break; case DEBUGVIEW_AXF_SURFACEDATA_SPECULAR_LOBE: - result = float3(surfacedata.specularLobe, 0.0); + result = surfacedata.specularLobe; break; case DEBUGVIEW_AXF_SURFACEDATA_HEIGHT: result = surfacedata.height_mm.xxx; @@ -162,6 +176,12 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res { switch (paramId) { + case DEBUGVIEW_AXF_BSDFDATA_AMBIENT_OCCLUSION: + result = bsdfdata.ambientOcclusion.xxx; + break; + case DEBUGVIEW_AXF_BSDFDATA_SPECULAR_OCCLUSION: + result = bsdfdata.specularOcclusion.xxx; + break; case DEBUGVIEW_AXF_BSDFDATA_NORMAL_WS: result = bsdfdata.normalWS * 0.5 + 0.5; break; @@ -184,7 +204,7 @@ void GetGeneratedBSDFDataDebug(uint paramId, BSDFData bsdfdata, inout float3 res result = bsdfdata.fresnelF0; break; case DEBUGVIEW_AXF_BSDFDATA_ROUGHNESS: - result = float3(bsdfdata.roughness, 0.0); + result = bsdfdata.roughness; break; case DEBUGVIEW_AXF_BSDFDATA_HEIGHT_MM: result = bsdfdata.height_mm.xxx; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.hlsl index bfe44fc8acc..97f7a90bca8 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.hlsl @@ -375,6 +375,31 @@ float GetScalarRoughnessFromAnisoRoughness(float roughnessT, float roughnessB) return 0.5 * (roughnessT + roughnessB); } +float GetScalarRoughness(float3 roughness) +{ + float singleRoughness = 0.5; + +#if defined(_AXF_BRDF_TYPE_SVBRDF) + + singleRoughness = (HasAnisotropy()) ? GetScalarRoughnessFromAnisoRoughness(roughness.x, roughness.y) : roughness.x; + +#elif defined(_AXF_BRDF_TYPE_CAR_PAINT) + float sumCoeffXRoughness = 0.0; + float sumCoeff = 0.0; + UNITY_UNROLL + for (uint lobeIndex = 0; lobeIndex < CARPAINT2_LOBE_COUNT; lobeIndex++) // TODO remove all variable lobecnt code + { + float coeff = _CarPaint2_CTCoeffs[lobeIndex]; + float spread = roughness[lobeIndex]; + sumCoeff += coeff; + sumCoeffXRoughness += spread * coeff; + } + singleRoughness = min(1.0, SafeDiv(sumCoeffXRoughness,sumCoeff)); +#endif + + return singleRoughness; +} + NormalData ConvertSurfaceDataToNormalData(SurfaceData surfaceData) { NormalData normalData; @@ -389,31 +414,9 @@ NormalData ConvertSurfaceDataToNormalData(SurfaceData surfaceData) { normalData.normalWS = surfaceData.normalWS; -#if defined(_AXF_BRDF_TYPE_SVBRDF) - float roughness = (HasAnisotropy()) ? GetScalarRoughnessFromAnisoRoughness(surfaceData.specularLobe.x, surfaceData.specularLobe.y) : surfaceData.specularLobe.x; - normalData.perceptualRoughness = RoughnessToPerceptualRoughness(roughness); - -#elif defined(_AXF_BRDF_TYPE_CAR_PAINT) // Hack: try to get a "single equivalent" roughness - normalData.perceptualRoughness = 0.0; - - float sumCoeffXRoughness = 0.0; - float sumCoeff = 0.0; - - UNITY_UNROLL - for (uint lobeIndex = 0; lobeIndex < CARPAINT2_LOBE_COUNT; lobeIndex++) - { - float coeff = _CarPaint2_CTCoeffs[lobeIndex]; - float spread = _CarPaint2_CTSpreads[lobeIndex]; - - sumCoeff += coeff; - sumCoeffXRoughness += spread * coeff; - } - normalData.perceptualRoughness = RoughnessToPerceptualRoughness(min(1.0, SafeDiv(sumCoeffXRoughness,sumCoeff))); -#else - // This is only possible if the AxF is a BTF type. However, there is a bunch of ifdefs do not support this third case - normalData.perceptualRoughness = 0.0; -#endif + float roughness = GetScalarRoughness(surfaceData.specularLobe); + normalData.perceptualRoughness = RoughnessToPerceptualRoughness(roughness); } return normalData; @@ -487,6 +490,7 @@ float3 RefractSaturateToTIR(float3 incoming, float3 normal, float eta, out floa float sinThetaCrit = saturate(rcp(eta)); float cosThetaCrit = sqrt(1 - Sq(sinThetaCrit)); float3 incOrthoN = (incoming - c * normal) * /*normalize the ortho component:*/rcp(sqrt(sinIncSq)); + // Note: sqrt(sinIncSq) shouldn't be close to 0, since b < 0 <=> (sinIncSq) > 1/Sq(eta) and eta shouldn't be close to 1/sqrt(eps)! criticalDir = sinThetaCrit * incOrthoN + cosThetaCrit * normal; @@ -543,7 +547,7 @@ float CT_F(float H_V, float F0) return F0 + (1.0 - F0) * f_1_sub_cos_fifth; } -float MultiLobesCookTorrance(float NdotL, float NdotV, float NdotH, float VdotH) +float MultiLobesCookTorrance(BSDFData bsdfData, float NdotL, float NdotV, float NdotH, float VdotH) { // Ensure numerical stability if (NdotV < 0.00174532836589830883577820272085 || NdotL < 0.00174532836589830883577820272085) //sin(0.1 deg ) @@ -554,7 +558,7 @@ float MultiLobesCookTorrance(float NdotL, float NdotV, float NdotH, float VdotH { float F0 = _CarPaint2_CTF0s[lobeIndex]; float coeff = _CarPaint2_CTCoeffs[lobeIndex]; - float spread = _CarPaint2_CTSpreads[lobeIndex]; + float spread = bsdfData.roughness[lobeIndex]; // _CarPaint2_CTSpreads[lobeIndex]; specularIntensity += coeff * CT_D(NdotH, spread) * CT_F(VdotH, F0); } @@ -684,10 +688,14 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData) BSDFData bsdfData; // ZERO_INITIALIZE(BSDFData, data); + bsdfData.ambientOcclusion = surfaceData.ambientOcclusion; + bsdfData.specularOcclusion = surfaceData.specularOcclusion; + bsdfData.normalWS = surfaceData.normalWS; bsdfData.tangentWS = surfaceData.tangentWS; bsdfData.biTangentWS = cross(bsdfData.normalWS, bsdfData.tangentWS); + bsdfData.roughness = 0; //----------------------------------------------------------------------------- #ifdef _AXF_BRDF_TYPE_SVBRDF bsdfData.diffuseColor = surfaceData.diffuseColor; @@ -696,7 +704,7 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData) bsdfData.fresnelF0 = surfaceData.fresnelF0; // See AxfData.hlsl: the actual sampled texture is always 1 channel, if we ever find otherwise, we will use the others. bsdfData.height_mm = surfaceData.height_mm; - bsdfData.roughness = HasAnisotropy() ? surfaceData.specularLobe : surfaceData.specularLobe.xx; + bsdfData.roughness.xy = HasAnisotropy() ? surfaceData.specularLobe.xy : surfaceData.specularLobe.xx; bsdfData.clearcoatColor = surfaceData.clearcoatColor; bsdfData.clearcoatNormalWS = HasClearcoat() ? surfaceData.clearcoatNormalWS : surfaceData.normalWS; @@ -717,7 +725,7 @@ BSDFData ConvertSurfaceDataToBSDFData(uint2 positionSS, SurfaceData surfaceData) bsdfData.specularColor = GetCarPaintSpecularColor(); bsdfData.fresnelF0 = GetCarPaintFresnelF0(); - bsdfData.roughness = 0; + bsdfData.roughness.xyz = surfaceData.specularLobe.xyz; // the later stores per lobe possibly modified (for geometric specular AA) _CarPaint2_CTSpreads bsdfData.height_mm = 0; #endif @@ -744,8 +752,8 @@ struct PreLightData float3 viewWS_UnderCoat; // View vector after optional clear-coat refraction. // IBL - float3 iblDominantDirectionWS_UnderCoat; // Dominant specular direction, used for IBL in EvaluateBSDF_Env() - float3 iblDominantDirectionWS_Clearcoat; // Dominant specular direction, used for IBL in EvaluateBSDF_Env() and also in area lights when clearcoat is enabled + float3 iblDominantDirectionWS_BottomLobeOnTop; // Dominant specular direction, for bottom lobe but as it exit on top, used for IBL in EvaluateBSDF_Env() + float3 iblDominantDirectionWS_Clearcoat; // Dominant specular direction, used for IBL in EvaluateBSDF_Env() and also in area lights when clearcoat is enabled #ifdef _AXF_BRDF_TYPE_SVBRDF float iblPerceptualRoughness; float3 specularFGD; @@ -983,7 +991,7 @@ float3 CarPaint_BTF(float thetaH, float thetaD, BSDFData bsdfData) } #endif //...#if defined(_AXF_BRDF_TYPE_CAR_PAINT) -float3 FindAverageBaseLobeDirOnTop(BSDFData bsdfData, PreLightData preLightData) +float3 FindAverageBaseLobeDirOnTop(BSDFData bsdfData, PreLightData preLightData, out float3 lobeDirUndercoat) { float3 outDir; @@ -1010,6 +1018,7 @@ float3 FindAverageBaseLobeDirOnTop(BSDFData bsdfData, PreLightData preLightData) float3 incomingSaturated; float rayIntensity; outDir = RefractSaturateToTIR(-vRefractedBottomReflected, -bsdfData.clearcoatNormalWS, bsdfData.clearcoatIOR, rayIntensity, incomingSaturated); + lobeDirUndercoat = -incomingSaturated; // incoming is away from the top interface from under the surface so *-1 to reverse quadrant. #endif return outDir; @@ -1045,13 +1054,19 @@ PreLightData GetPreLightData(float3 viewWS_Clearcoat, PositionInputs posInput // Handle IBL + multiscattering // todo_dir: // todo_dir todo_modes todo_pseudorefract: cant use undercoat like that, but better than to lose the bottom normal effect for now... - preLightData.iblDominantDirectionWS_UnderCoat = reflect(-preLightData.viewWS_UnderCoat, bsdfData.normalWS); + float3 reflectedLobeDirUndercoat = reflect(-preLightData.viewWS_UnderCoat, bsdfData.normalWS); + preLightData.iblDominantDirectionWS_BottomLobeOnTop = reflectedLobeDirUndercoat; if (HasClearcoatAndRefraction()) { - preLightData.iblDominantDirectionWS_UnderCoat = FindAverageBaseLobeDirOnTop(bsdfData, preLightData); // much better + preLightData.iblDominantDirectionWS_BottomLobeOnTop = FindAverageBaseLobeDirOnTop(bsdfData, preLightData, reflectedLobeDirUndercoat); // much better + // reflectedLobeDirUndercoat is now adjusted to correspond to the refracted-back on top direction returned by FindAverageBaseLobeDirOnTop() + + //sanity check: If both normals are equal, then this shouldn't change the output: + //preLightData.iblDominantDirectionWS_BottomLobeOnTop = reflect(-viewWS_Clearcoat, bsdfData.clearcoatNormalWS); + //reflectedLobeDirUndercoat = reflect(-preLightData.viewWS_UnderCoat, bsdfData.normalWS); } preLightData.iblDominantDirectionWS_Clearcoat = reflect(-viewWS_Clearcoat, bsdfData.clearcoatNormalWS); - //preLightData.iblDominantDirectionWS_UnderCoat = preLightData.iblDominantDirectionWS_Clearcoat; + //preLightData.iblDominantDirectionWS_BottomLobeOnTop = preLightData.iblDominantDirectionWS_Clearcoat; #ifdef _AXF_BRDF_TYPE_SVBRDF // @TODO => Anisotropic IBL? @@ -1103,10 +1118,31 @@ PreLightData GetPreLightData(float3 viewWS_Clearcoat, PositionInputs posInput preLightData.specularCTFGDReflectivity = 0; preLightData.ltcTransformSpecularCT = (float3x3[MAX_CT_LOBE_COUNT])0; - // TODO_diffuseFGDColor: better one, averaged maybe... + // TODO_diffuseFGDColor: better one, averaged maybe: ie depending on roughness also preLightData.singleBRDFColor = 1.0; float thetaH = 0; //acos(clamp(NdotH, 0, 1)); float thetaD = acos(clamp(preLightData.NdotV_UnderCoat, 0, 1)); + // The above is the same as + //float3 lightDir = reflect(-preLightData.viewWS_UnderCoat, bsdfData.normalWS); + //float3 H = normalize(preLightData.viewWS_UnderCoat + lightDir); + //float NdotH = dot(bsdfData.normalWS, H); + //float LdotH = dot(H, lightDir); + //thetaH = acos(clamp(NdotH, 0, 1)); + //thetaD = acos(clamp(LdotH, 0, 1)); + + // Also, could use reflectedLobeDirUndercoat here (and see TODO_diffuseFGDColor: if we make it depends on roughness, one per lobe) + // This is relevant only if both normals aren't the same obviously. + // In the case of CARPAINT, this means a clearcoat normal map. + // (ie orange peel) + if (false) + { + float3 H = normalize(preLightData.viewWS_UnderCoat + reflectedLobeDirUndercoat); + float NdotH = dot(bsdfData.normalWS, H); + + float LdotH = dot(H, reflectedLobeDirUndercoat); + thetaH = acos(clamp(NdotH, 0, 1)); + thetaD = acos(clamp(LdotH, 0, 1)); + } preLightData.singleBRDFColor *= GetBRDFColor(thetaH, thetaD); preLightData.singleFlakesComponent = CarPaint_BTF(thetaH, thetaD, bsdfData); @@ -1116,7 +1152,7 @@ PreLightData GetPreLightData(float3 viewWS_Clearcoat, PositionInputs posInput { float F0 = _CarPaint2_CTF0s[lobeIndex]; float coeff = _CarPaint2_CTCoeffs[lobeIndex]; - float spread = _CarPaint2_CTSpreads[lobeIndex]; + float spread = bsdfData.roughness[lobeIndex]; // _CarPaint2_CTSpreads[lobeIndex]; #if !USE_COOK_TORRANCE_MULTI_LOBES // Computes weighted average of roughness values sumCoeff += coeff; @@ -1161,8 +1197,9 @@ PreLightData GetPreLightData(float3 viewWS_Clearcoat, PositionInputs posInput float oneOverLobeCnt = rcp(CARPAINT2_LOBE_COUNT); preLightData.iblPerceptualRoughness = RoughnessToPerceptualRoughness(sumRoughness * oneOverLobeCnt); tempF0 = sumF0 * oneOverLobeCnt; - // todo_BeckmannToGGX + // todo_BeckmannToGGX GetPreIntegratedFGDCookTorranceAndLambert(NdotV_UnderCoat, preLightData.iblPerceptualRoughness, tempF0 * preLightData.singleBRDFColor, specularFGD, diffuseFGD, reflectivity); + preLightData.iblPerceptualRoughness = PerceptualRoughnessBeckmannToGGX(preLightData.iblPerceptualRoughness); specularFGD *= GetPreIntegratedFGDCookTorranceSampleMutiplier(); preLightData.specularCTFGDSingleLobe = specularFGD * sumCoeff; #endif @@ -1415,7 +1452,7 @@ float3 ComputeWard(float3 H, float LdotH, float NdotL, float NdotV, PreLightData float F = 1.0; switch (_SVBRDF_BRDFVariants & 3) { - case 1: F_FresnelDieletricSafe(Fresnel0ToIorSafe(bsdfData.fresnelF0.r), LdotH); break; + case 1: F = F_FresnelDieletricSafe(Fresnel0ToIorSafe(bsdfData.fresnelF0.r), LdotH); break; case 2: F = F_Schlick(bsdfData.fresnelF0.r, LdotH); break; } @@ -1423,8 +1460,8 @@ float3 ComputeWard(float3 H, float LdotH, float NdotL, float NdotV, PreLightData float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.biTangentWS), dot(H, bsdfData.normalWS)); //float2 rotH = tsH.xy / tsH.z; float2 rotH = tsH.xy / max(0.00001, tsH.z); - //float2 roughness = bsdfData.roughness; - float2 roughness = max(0.0001, bsdfData.roughness); + //float2 roughness = bsdfData.roughness.xy; + float2 roughness = max(0.0001, bsdfData.roughness.xy); //if (bsdfData.roughness.y == 0.0) bsdfData.specularColor = float3(1,0,0); if (roughness.x * roughness.y <= 0.0001 && tsH.z < 1.0) @@ -1448,7 +1485,7 @@ float3 ComputeWard(float3 H, float LdotH, float NdotL, float NdotV, PreLightData float3 ComputeBlinnPhong(float3 H, float LdotH, float NdotL, float NdotV, PreLightData preLightData, BSDFData bsdfData) { - float2 exponents = exp2(bsdfData.roughness); + float2 exponents = exp2(bsdfData.roughness.xy); // Evaluate normal distribution function float3 tsH = float3(dot(H, bsdfData.tangentWS), dot(H, bsdfData.biTangentWS), dot(H, bsdfData.normalWS)); @@ -1619,6 +1656,7 @@ CBSDF EvaluateBSDF(float3 viewWS_Clearcoat, float3 lightWS_Clearcoat, PreLightDa float3 GetCarPaintSpecularFGDForLobe(PreLightData preLightData, uint lobeIndex) { return lerp(preLightData.specularCTFGDAtZeroF0[lobeIndex], preLightData.specularCTFGDReflectivity[lobeIndex], _CarPaint2_CTF0s[lobeIndex]*preLightData.singleBRDFColor); + //return lerp(preLightData.specularCTFGDAtZeroF0[lobeIndex], preLightData.specularCTFGDReflectivity[lobeIndex], _CarPaint2_CTF0s[lobeIndex])*preLightData.singleBRDFColor; } @@ -1685,7 +1723,7 @@ CBSDF EvaluateBSDF(float3 viewWS_Clearcoat, float3 lightWS_Clearcoat, PreLightDa float3 diffuseTerm = Lambert(); // Apply multi-lobes Cook-Torrance - float3 specularTerm = MultiLobesCookTorrance(NdotL, NdotV, NdotH, VdotH); + float3 specularTerm = MultiLobesCookTorrance(bsdfData, NdotL, NdotV, NdotH, VdotH); // Apply BRDF color float3 BRDFColor = GetBRDFColor(thetaH, thetaD); @@ -1959,7 +1997,7 @@ DirectLighting EvaluateBSDF_Line( LightLoopContext lightLoopContext, // We project the point onto the area light's plane using the reflected view direction and recompute the light direction from this position // todo_dir: #if 0 - float3 bestLightWS_Specular = ComputeBestLightDirection_Line(lightPositionRWS, preLightData.iblDominantDirectionWS_UnderCoat, lightData); + float3 bestLightWS_Specular = ComputeBestLightDirection_Line(lightPositionRWS, preLightData.iblDominantDirectionWS_BottomLobeOnTop, lightData); // todo_dir todo_pseudorefract // refract light dir here for GetBRDFColor since it is a fresnel-like effect, but @@ -2178,7 +2216,7 @@ DirectLighting EvaluateBSDF_Rect(LightLoopContext lightLoopContext, // We project the point onto the area light's plane using the reflected view direction and recompute the light direction from this position // TODO_dir: #if 0 - float3 bestLightWS_Specular = ComputeBestLightDirection_Rectangle(lightPositionRWS, preLightData.iblDominantDirectionWS_UnderCoat, lightData); + float3 bestLightWS_Specular = ComputeBestLightDirection_Rectangle(lightPositionRWS, preLightData.iblDominantDirectionWS_BottomLobeOnTop, lightData); // TODO_dir: refract light dir for GetBRDFColor here since it is a fresnel-like effect, but // compute LTC / env fetching using *non refracted dir* @@ -2398,24 +2436,43 @@ IndirectLighting EvaluateBSDF_Env( LightLoopContext lightLoopContext, float weight = 1.0; // TODO_dir: this shouldn't be undercoat. - float3 environmentSamplingDirectionWS_UnderCoat = preLightData.iblDominantDirectionWS_UnderCoat; + float3 envSamplingDirForBottomLayer = preLightData.iblDominantDirectionWS_BottomLobeOnTop; #if defined(_AXF_BRDF_TYPE_SVBRDF) float3 envLighting = 0.0; float NdotV = ClampNdotV(preLightData.NdotV_UnderCoat); - - environmentSamplingDirectionWS_UnderCoat = GetModifiedEnvSamplingDir(lightData, bsdfData.normalWS, preLightData.iblDominantDirectionWS_UnderCoat, preLightData.iblPerceptualRoughness, NdotV); - - // Note: using _influenceShapeType and projectionShapeType instead of (lightData|proxyData).shapeType allow to make compiler optimization in case the type is know (like for sky) - EvaluateLight_EnvIntersection(positionWS, bsdfData.normalWS, lightData, _influenceShapeType, environmentSamplingDirectionWS_UnderCoat, weight); + // Here we use bsdfData.clearcoatNormalWS: if there's no coat, bsdfData.clearcoatNormalWS == bsdfData.normalWS anyway. + // The reason is that, normally, since GetModifiedEnvSamplingDir (off-specular effect) is roughness dependent, + // we would have to store another direction (lightData is only used to escape the modification in case of planar probe) + // and in case of carpaint, one for each lobe. However, if we would like to "correctly" take into account the effect, we would have + // to calculate the effect on the bottom layer where directions are different, and then use FindAverageBaseLobeDirOnTop(). + // We decide to just apply the effect on top instead. + // (FindAverageBaseLobeDirOnTop is alreayd an approximation ignoring under-horizon or TIR. If we saturated to the critical angle undercoat + // and thus grazing when exiting on top, a tilt back for off-specular effect might in fact have no effect since the lobe could still + // be under horizon. On the other hand, if we didn't have to saturate, a little tilt-back toward normal (from GetModifiedEnvSamplingDir) + // should have translated into a bigger one on top because of angle range decompression.) + envSamplingDirForBottomLayer = GetModifiedEnvSamplingDir(lightData, bsdfData.clearcoatNormalWS, preLightData.iblDominantDirectionWS_BottomLobeOnTop, preLightData.iblPerceptualRoughness, NdotV); + + // Note: using _influenceShapeType and projectionShapeType instead of (lightData|proxyData).shapeType allow to make compiler optimization in case the type is know (like for sky) + EvaluateLight_EnvIntersection(positionWS, bsdfData.clearcoatNormalWS, lightData, _influenceShapeType, envSamplingDirForBottomLayer, weight); + // ...here the normal is only used for normal fading mode of the influence volume. + + // Another problem with having even two fetch directions is the reflection hierarchy that only supports one weight. + // (TODO: We could have a vector tracking multiplied weights already applied per lobe that we update and that is + // passed back by the light loop but otherwise opaque to it, with the single hierarchyWeight tracked alongside. + // That way no "overlighting" would be done and by returning the hierarchyWeight = min(all weights) up to now, + // we could potentially avoid artifacts in having eg the clearcoat reflection not available from one influence volume + // while the base has full weight reflection. This ends up always preventing a blend for the coat reflection when the + // bottom reflection is full. Lit doesn't have this problem too much in practice since only GetModifiedEnvSamplingDir + // changes the direction vs the coat.) float IBLMipLevel; IBLMipLevel = GetEnvMipLevel(lightData, preLightData.iblPerceptualRoughness); // Sample the pre-integrated environment lighting - float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, environmentSamplingDirectionWS_UnderCoat, IBLMipLevel, lightData.rangeCompressionFactorCompensation); + float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, envSamplingDirForBottomLayer, IBLMipLevel, lightData.rangeCompressionFactorCompensation); weight *= preLD.w; // Used by planar reflection to discard pixel envLighting = GetSpecularIndirectDimmer() * preLightData.specularFGD * preLD.xyz; @@ -2425,39 +2482,37 @@ IndirectLighting EvaluateBSDF_Env( LightLoopContext lightLoopContext, float3 envLighting = 0.0; - float NdotV = ClampNdotV(preLightData.NdotV_UnderCoat); - // A part of this BRDF depends on thetaH and thetaD and should thus have entered // the split sum pre-integration. We do a further approximation by pulling those // terms out and evaluating them in the specular dominant direction, - // for BRDFColor and flakes. - float3 viewWS_UnderCoat = preLightData.viewWS_UnderCoat; - float3 lightWS_UnderCoat = environmentSamplingDirectionWS_UnderCoat; + // for BRDFColor and flakes, see GetPreLightData. - float3 H = normalize(viewWS_UnderCoat + lightWS_UnderCoat); - float NdotH = dot(bsdfData.normalWS, H); - float VdotH = dot(viewWS_UnderCoat, H); - - // TODO_dir: so this is just thetaH = 0, etc. CHECK and remove. - float thetaH = acos(clamp(NdotH, 0, 1)); - float thetaD = acos(clamp(VdotH, 0, 1)); + // Note: we don't use GetModifiedEnvSamplingDir() per lobe here, and see comment above about reflection hierarchy. + EvaluateLight_EnvIntersection(positionWS, bsdfData.clearcoatNormalWS, lightData, _influenceShapeType, envSamplingDirForBottomLayer, weight); #if USE_COOK_TORRANCE_MULTI_LOBES // Multi-lobes approach // Each CT lobe samples the environment with the appropriate roughness - float sumWeights = 0.0; + float probeSkipFactor = 1; for (uint lobeIndex = 0; lobeIndex < CARPAINT2_LOBE_COUNT; lobeIndex++) { float coeff = _CarPaint2_CTCoeffs[lobeIndex]; float lobeMipLevel = PerceptualRoughnessToMipmapLevel(preLightData.iblPerceptualRoughness[lobeIndex]); - float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, lightWS_UnderCoat, lobeMipLevel, lightData.rangeCompressionFactorCompensation); + float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, envSamplingDirForBottomLayer, lobeMipLevel, lightData.rangeCompressionFactorCompensation); //todotodo: try removing coeff envLighting += coeff * GetCarPaintSpecularFGDForLobe(preLightData, lobeIndex) * preLD.xyz; - sumWeights += preLD.w; + // Note: preLD.w is only used by planar probes, returning 0 if outside captured direction or 1 otherwise (the influence volume weight fades, not this). + // Since this is only used for planar probes, even if we had used GetModifiedEnvSamplingDir() above, all directions would be the same in that case anyway + // since GetModifiedEnvSamplingDir() doesn't do anything for planar probes. + // For that reason, only one preLD.w needs to be used, no need to average them, they should all be the same. + // sumWeights += preLD.w; + probeSkipFactor = preLD.w; } + // See discussion about reflection hierarchy above for SVBRDF, same thing here: When we will evaluate the coat, we will ignore its weight. + weight *= probeSkipFactor; envLighting *= GetSpecularIndirectDimmer(); //now already in rebuilt specularFGD: envLighting *= GetBRDFColor(thetaH, thetaD); @@ -2465,8 +2520,7 @@ IndirectLighting EvaluateBSDF_Env( LightLoopContext lightLoopContext, //TODO_FLAKES float flakesMipLevel = 0; // Flakes are supposed to be perfect mirrors //envLighting += preLightData.flakesFGD * CarPaint_BTF(thetaH, thetaD, bsdfData) * SampleEnv(lightLoopContext, lightData.envIndex, lightWS_UnderCoat, flakesMipLevel, lightData.rangeCompressionFactorCompensation).xyz; - envLighting += preLightData.singleFlakesComponent * SampleEnv(lightLoopContext, lightData.envIndex, lightWS_UnderCoat, flakesMipLevel, lightData.rangeCompressionFactorCompensation).xyz; - weight *= sumWeights / CARPAINT2_LOBE_COUNT; + envLighting += preLightData.singleFlakesComponent * SampleEnv(lightLoopContext, lightData.envIndex, envSamplingDirForBottomLayer, flakesMipLevel, lightData.rangeCompressionFactorCompensation).xyz; #else // USE_COOK_TORRANCE_MULTI_LOBES @@ -2476,10 +2530,10 @@ IndirectLighting EvaluateBSDF_Env( LightLoopContext lightLoopContext, IBLMipLevel = GetEnvMipLevel(lightData, preLightData.iblPerceptualRoughness); // Sample the actual environment lighting - float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, lightWS_UnderCoat, IBLMipLevel, lightData.rangeCompressionFactorCompensation); + float4 preLD = SampleEnv(lightLoopContext, lightData.envIndex, envSamplingDirForBottomLayer, IBLMipLevel, lightData.rangeCompressionFactorCompensation); float3 envLighting; - envLighting = preLightData.specularCTFGDSingleLobe * GetSpecularIndirectDimmer() * GetBRDFColor(thetaH, thetaD); + envLighting = preLightData.specularCTFGDSingleLobe * GetSpecularIndirectDimmer(); //TODO_FLAKES //envLighting += preLightData.flakesFGD * CarPaint_BTF(thetaH, thetaD, bsdfData); envLighting += preLightData.singleFlakesComponent; @@ -2534,9 +2588,11 @@ void PostEvaluateBSDF( LightLoopContext lightLoopContext, { // There is no AmbientOcclusion from data with AxF, but let's apply our SSAO AmbientOcclusionFactor aoFactor; - GetScreenSpaceAmbientOcclusionMultibounce( posInput.positionSS, preLightData.NdotV_UnderCoat, - RoughnessToPerceptualRoughness(GetScalarRoughnessFromAnisoRoughness(bsdfData.roughness.x, bsdfData.roughness.y)), - 1.0, 1.0, GetColorBaseDiffuse(bsdfData), GetColorBaseFresnelF0(bsdfData), aoFactor); + GetScreenSpaceAmbientOcclusionMultibounce(posInput.positionSS, preLightData.NdotV_UnderCoat, + RoughnessToPerceptualRoughness(GetScalarRoughness(bsdfData.roughness)), + bsdfData.ambientOcclusion, bsdfData.specularOcclusion, + GetColorBaseDiffuse(bsdfData), GetColorBaseFresnelF0(bsdfData), aoFactor); + ApplyAmbientOcclusionFactor(aoFactor, builtinData, lighting); diffuseLighting = bsdfData.diffuseColor * lighting.direct.diffuse + builtinData.bakeDiffuseLighting; diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.shader b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.shader index 5bbc13d29d4..8590a6b6cb8 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.shader +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxF.shader @@ -63,6 +63,10 @@ Shader "HDRP/AxF" _CarPaint2_CTCoeffs("_CarPaint2_CTCoeffs", Vector) = (1,1,1,1) _CarPaint2_CTSpreads("_CarPaint2_CTSpreads", Vector) = (1,1,1,1) + // GUI inspector only - saves state in material meta, read back from SetupMaterialKeywordsAndPass + //[Enum(Off, 0, From Ambient Occlusion, 1, From Bent Normals, 2)] _SpecularOcclusionMode("Specular Occlusion Mode", Int) = 1 + [Enum(Off, 0, From Ambient Occlusion, 1)] _SpecularOcclusionMode("Specular Occlusion Mode", Int) = 1 + [ToggleUI] _UseShadowThreshold("_UseShadowThreshold", Float) = 0.0 [ToggleUI] _AlphaCutoffEnable("Alpha Cutoff Enable", Float) = 0.0 _AlphaCutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 @@ -102,6 +106,10 @@ Shader "HDRP/AxF" [Enum(Flip, 0, Mirror, 1, None, 2)] _DoubleSidedNormalMode("Double sided normal mode", Float) = 1 // This is for the editor only, see BaseLitUI.cs: _DoubleSidedConstants will be set based on the mode. [HideInInspector] _DoubleSidedConstants("_DoubleSidedConstants", Vector) = (1, 1, -1, 0) + [ToggleUI] _EnableGeometricSpecularAA("EnableGeometricSpecularAA", Float) = 0.0 + _SpecularAAScreenSpaceVariance("SpecularAAScreenSpaceVariance", Range(0.0, 1.0)) = 0.1 + _SpecularAAThreshold("SpecularAAThreshold", Range(0.0, 1.0)) = 0.2 + // Caution: C# code in BaseLitUI.cs call LightmapEmissionFlagsProperty() which assume that there is an existing "_EmissionColor" // value that exist to identify if the GI emission need to be enabled. // In our case we don't use such a mechanism but need to keep the code quiet. We declare the value and always enable it. @@ -130,12 +138,15 @@ Shader "HDRP/AxF" //------------------------------------------------------------------------------------- #pragma shader_feature_local _AXF_BRDF_TYPE_SVBRDF _AXF_BRDF_TYPE_CAR_PAINT _AXF_BRDF_TYPE_BTF + #pragma shader_feature_local _ _SPECULAR_OCCLUSION_NONE //_SPECULAR_OCCLUSION_FROM_BENT_NORMAL_MAP + #pragma shader_feature_local _ALPHATEST_ON #pragma shader_feature_local _ALPHATOMASK_ON #pragma shader_feature_local _DOUBLESIDED_ON #pragma shader_feature_local _DISABLE_DECALS #pragma shader_feature_local _DISABLE_SSR + #pragma shader_feature_local _ENABLE_GEOMETRIC_SPECULAR_AA #pragma shader_feature_local _ADD_PRECOMPUTED_VELOCITY diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFData.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFData.hlsl index edf69c325a8..48fd3724431 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFData.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFData.hlsl @@ -38,6 +38,9 @@ void ApplyDecalToSurfaceData(DecalSurfaceData decalSurfaceData, inout SurfaceDat surfaceData.specularLobe.x = PerceptualSmoothnessToRoughness(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.x) * decalSurfaceData.mask.w + decalSurfaceData.mask.z); surfaceData.specularLobe.y = PerceptualSmoothnessToRoughness(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.y) * decalSurfaceData.mask.w + decalSurfaceData.mask.z); +#ifdef _AXF_BRDF_TYPE_CAR_PAINT + surfaceData.specularLobe.z = PerceptualSmoothnessToRoughness(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.z) * decalSurfaceData.mask.w + decalSurfaceData.mask.z); +#endif } #endif } @@ -60,11 +63,15 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p float alpha = 1.0; + surfaceData.ambientOcclusion = 1.0; + surfaceData.specularOcclusion = 1.0; + surfaceData.specularLobe = 0; + #ifdef _AXF_BRDF_TYPE_SVBRDF surfaceData.diffuseColor = SAMPLE_TEXTURE2D(_SVBRDF_DiffuseColorMap, sampler_SVBRDF_DiffuseColorMap, UV0).xyz; surfaceData.specularColor = SAMPLE_TEXTURE2D(_SVBRDF_SpecularColorMap, sampler_SVBRDF_SpecularColorMap, UV0).xyz; - surfaceData.specularLobe = _SVBRDF_SpecularLobeMapScale * SAMPLE_TEXTURE2D(_SVBRDF_SpecularLobeMap, sampler_SVBRDF_SpecularLobeMap, UV0).xy; + surfaceData.specularLobe.xy = _SVBRDF_SpecularLobeMapScale * SAMPLE_TEXTURE2D(_SVBRDF_SpecularLobeMap, sampler_SVBRDF_SpecularLobeMap, UV0).xy; // The AxF models include both a general coloring term that they call "specular color" while the f0 is actually another term, // seemingly always scalar: @@ -98,6 +105,8 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p surfaceData.diffuseColor = _CarPaint2_CTDiffuse; surfaceData.clearcoatIOR = max(1.001, _CarPaint2_ClearcoatIOR); // Can't be exactly 1 otherwise the precise fresnel divides by 0! + surfaceData.specularLobe = _CarPaint2_CTSpreads.xyz; // We may want to modify these (eg for Specular AA) + surfaceData.normalWS = input.tangentToWorld[2].xyz; GetNormalWS(input, 2.0 * SAMPLE_TEXTURE2D(_ClearcoatNormalMap, sampler_ClearcoatNormalMap, UV0).xyz - 1.0, surfaceData.clearcoatNormalWS, doubleSidedConstants); @@ -116,13 +125,29 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p // Useless for car paint BSDF surfaceData.specularColor = 0; - surfaceData.specularLobe = 0; surfaceData.fresnelF0 = 0; surfaceData.height_mm = 0; surfaceData.anisotropyAngle = 0; surfaceData.clearcoatColor = 0; #endif + // TODO + // Assume same xyz encoding for AxF bent normal as other normal maps. + //float3 bentNormalWS; + //GetNormalWS(input, 2.0 * SAMPLE_TEXTURE2D(_BentNormalMap, sampler_BentNormalMap, UV0).xyz - 1.0, bentNormalWS, doubleSidedConstants); + + float perceptualRoughness = RoughnessToPerceptualRoughness(GetScalarRoughness(surfaceData.specularLobe)); + + //TODO +//#if defined(_SPECULAR_OCCLUSION_FROM_BENT_NORMAL_MAP) + // Note: we use normalWS as it will always exist and be equal to clearcoatNormalWS if there's no coat + // (otherwise we do SO with the base lobe, might be wrong depending on way AO is computed, will be wrong either way with a single non-lobe specific value) + //surfaceData.specularOcclusion = GetSpecularOcclusionFromBentAO(V, bentNormalWS, surfaceData.normalWS, surfaceData.ambientOcclusion, perceptualRoughness); +//#endif +#if !defined(_SPECULAR_OCCLUSION_NONE) + surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(ClampNdotV(dot(surfaceData.normalWS, V)), surfaceData.ambientOcclusion, perceptualRoughness); +#endif + // Propagate the geometry normal surfaceData.geomNormalWS = input.tangentToWorld[2]; @@ -166,6 +191,15 @@ void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs p #endif #endif +#if defined(_ENABLE_GEOMETRIC_SPECULAR_AA) + // Specular AA for geometric curvature + + surfaceData.specularLobe.x = PerceptualSmoothnessToRoughness(GeometricNormalFiltering(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.x), input.tangentToWorld[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold)); + surfaceData.specularLobe.y = PerceptualSmoothnessToRoughness(GeometricNormalFiltering(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.y), input.tangentToWorld[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold)); +#if defined(_AXF_BRDF_TYPE_CAR_PAINT) + surfaceData.specularLobe.z = PerceptualSmoothnessToRoughness(GeometricNormalFiltering(RoughnessToPerceptualSmoothness(surfaceData.specularLobe.z), input.tangentToWorld[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold)); +#endif +#endif #if defined(DEBUG_DISPLAY) if (_DebugMipMapMode != DEBUGMIPMAPMODE_NONE) diff --git a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFProperties.hlsl b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFProperties.hlsl index 6f7a6527e3f..9c3383f73da 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFProperties.hlsl +++ b/com.unity.render-pipelines.high-definition/Runtime/Material/AxF/AxFProperties.hlsl @@ -107,6 +107,11 @@ float _UseShadowThreshold; float _AlphaCutoffShadow; float4 _DoubleSidedConstants; +// Specular AA +float _EnableGeometricSpecularAA; +float _SpecularAAScreenSpaceVariance; +float _SpecularAAThreshold; + // Caution: C# code in BaseLitUI.cs call LightmapEmissionFlagsProperty() which assume that there is an existing "_EmissionColor" // value that exist to identify if the GI emission need to be enabled. // In our case we don't use such a mechanism but need to keep the code quiet. We declare the value and always enable it.