浏览代码

Merge pull request #1411 from Unity-Technologies/StackLit2

Add support of texture normal filtering to stacklit
/main
GitHub 7 年前
当前提交
1ab5c8d6
共有 8 个文件被更改,包括 103 次插入56 次删除
  1. 32
      com.unity.render-pipelines.core/CoreRP/ShaderLibrary/CommonMaterial.hlsl
  2. 49
      com.unity.render-pipelines.high-definition/HDRP/Editor/AssetPostProcessors/NormalMapVarianceTexturePostprocessor.cs
  3. 18
      com.unity.render-pipelines.high-definition/HDRP/Editor/Material/StackLit/StackLitUI.cs
  4. 2
      com.unity.render-pipelines.high-definition/HDRP/Material/LayeredLit/LayeredLitData.hlsl
  5. 2
      com.unity.render-pipelines.high-definition/HDRP/Material/Lit/LitData.hlsl
  6. 3
      com.unity.render-pipelines.high-definition/HDRP/Material/StackLit/StackLit.shader
  7. 50
      com.unity.render-pipelines.high-definition/HDRP/Material/StackLit/StackLitData.hlsl
  8. 3
      com.unity.render-pipelines.high-definition/HDRP/Material/StackLit/StackLitProperties.hlsl

32
com.unity.render-pipelines.core/CoreRP/ShaderLibrary/CommonMaterial.hlsl


roughnessB = ClampRoughnessForAnalyticalLights(roughnessB);
}
// Use with stack BRDF (clear coat / coat)
// Use with stack BRDF (clear coat / coat) - This only used same equation to convert from Blinn-Phong spec power to Beckmann roughness
real RoughnessToVariance(real roughness)
{
return 2.0 / Sq(roughness) - 2.0;

return sqrt(2.0 / (variance + 2.0));
}
float FilterPerceptualSmoothness(float perceptualSmoothness, float variance, float threshold)
// Return modified perceptualSmoothness based on provided variance (get from GeometricNormalVariance + TextureNormalVariance)
float NormalFiltering(float perceptualSmoothness, float variance, float threshold)
// Ref: Geometry into Shading - http://graphics.pixar.com/library/BumpRoughness/paper.pdf - equation (3)
float squaredRoughness = saturate(roughness * roughness + min(2.0 * variance, threshold * threshold)); // threshold can be really low, square the value for easier control
return 1.0 - RoughnessToPerceptualRoughness(sqrt(squaredRoughness));

// This is the deferred approximation, which works reasonably well so we keep it for forward too for now.
// screenSpaceVariance should be at most 0.5^2 = 0.25, as that corresponds to considering
// a gaussian pixel reconstruction kernel with a standard deviation of 0.5 of a pixel, thus 2 sigma covering the whole pixel.
float GeometricFilterVariance(float3 geometricNormalWS, float screenSpaceVariance)
float GeometricNormalVariance(float3 geometricNormalWS, float screenSpaceVariance)
{
float3 deltaU = ddx(geometricNormalWS);
float3 deltaV = ddy(geometricNormalWS);

float GeometricFilterPerceptualSmoothness(float perceptualSmoothness, float3 geometricNormalWS, float screenSpaceVariance, float threshold)
// Return modified perceptualSmoothness
float GeometricNormalFiltering(float perceptualSmoothness, float3 geometricNormalWS, float screenSpaceVariance, float threshold)
float variance = GeometricFilterVariance(geometricNormalWS, screenSpaceVariance);
return FilterPerceptualSmoothness(perceptualSmoothness, variance, threshold);
float variance = GeometricNormalVariance(geometricNormalWS, screenSpaceVariance);
return NormalFiltering(perceptualSmoothness, variance, threshold);
}
// Normal map filtering based on The Order : 1886 SIGGRAPH course notes implementation.

// Note that hw filtering on the normal map should be trilinear to be conservative, while anisotropic
// risk underfiltering. Could also compute average normal on the fly with a proper normal map format,
// like Toksvig.
float TextureFilteringVariance(float avgNormalLength)
float TextureNormalVariance(float avgNormalLength)
{
if (avgNormalLength < 1.0)
{

// Ref: Frequency Domain Normal Map Filtering - http://www.cs.columbia.edu/cg/normalmap/normalmap.pdf (equation 21)
// Relationship between between the standard deviation of a Gaussian distribution and the roughness parameter of a Beckmann distribution.
// is roughness^2 = 2 variance (note: variance is sigma^2)
// (Ref: Filtering Distributions of Normals for Shading Antialiasing - Equation just after (14))
// so to get variance we must use variance = 1 / (4 * kappa)
// (Equation 36 of Normal map filtering based on The Order : 1886 SIGGRAPH course notes implementation).
// So to get variance we must use variance = 1 / (4 * kappa)
return 0.0f;
return 0.0;
float TextureFilterPerceptualSmoothness(float perceptualSmoothness, float avgNormalLength, float threshold)
float TextureNormalFiltering(float perceptualSmoothness, float avgNormalLength, float threshold)
float variance = TextureFilteringVariance(avgNormalLength);
return FilterPerceptualSmoothness(perceptualSmoothness, variance, threshold);
float variance = TextureNormalVariance(avgNormalLength);
return NormalFiltering(perceptualSmoothness, variance, threshold);
}
// ----------------------------------------------------------------------------

49
com.unity.render-pipelines.high-definition/HDRP/Editor/AssetPostProcessors/NormalMapVarianceTexturePostprocessor.cs


using UnityEditor;
using UnityEngine;
public class NormalMapVarianceTexturePostprocessor : AssetPostprocessor
public class NormalMapAverageLengthTexturePostprocessor : AssetPostprocessor
// This class will process a normal map and add the value of average normal length to the blue or alpha channel
// The texture is save as BC7.
// Tangent space normal map: BC7 RGB (normal xy - average normal length)
// Object space normal map: BC7 RGBA (normal xyz - average normal length)
static string s_Suffix = "_NA";
//static string s_SuffixOS = "_OSNA"; // Suffix for object space case - TODO
if (assetPath.IndexOf("_variance", StringComparison.InvariantCultureIgnoreCase) != -1)
// Any texture with _NA suffix will store average normal lenght in alpha
if (assetPath.IndexOf(s_Suffix, StringComparison.InvariantCultureIgnoreCase) != -1)
textureImporter.textureCompression = TextureImporterCompression.CompressedHQ;
textureImporter.alphaSource = TextureImporterAlphaSource.None;
textureImporter.mipmapEnabled = true;
textureImporter.textureCompression = TextureImporterCompression.CompressedHQ; // This is BC7 for Mac/PC
#pragma warning disable 618 // remove obsolete warning for this one
textureImporter.linearTexture = true; // Says deprecated but won't work without it.

void OnPostprocessTexture(Texture2D texture)
{
if (assetPath.IndexOf("_variance", StringComparison.InvariantCultureIgnoreCase) != -1)
if (assetPath.IndexOf(s_Suffix, StringComparison.InvariantCultureIgnoreCase) != -1)
// For mip 0, set the normal length to 1.
{
Color[] c = texture.GetPixels(0);
for (int i = 0; i < c.Length; i++)
{
c[i].a = 1.0f;
}
texture.SetPixels(c, 0);
}
// Based on The Order : 1886 SIGGRAPH course notes implementation. Sample all normal map
// texels from the base mip level that are within the footprint of the current mipmap texel.
Color[] source = texture.GetPixels(0);

// Store the normal length for the average normal.
int outputPosition = y * mipWidth + x;
//c[outputPosition].a = averageNormal.magnitude;
// Clamp to avoid any issue (TODO: Check this)
// Write into the blue channel
float averageNormalLength = Math.Max(0.0f, Math.Min(1.0f, averageNormal.magnitude));
float normalLength = Math.Max(0.0f, Math.Min(1.0f, averageNormal.magnitude));
c[outputPosition].a = normalLength;
c[outputPosition].b = averageNormalLength;
c[outputPosition].a = 1.0f;
}
// Now overwrite the first mip average normal channel - order is important as above we read the mip0
// For mip 0, set the normal length to 1.
{
Color[] c = texture.GetPixels(0);
for (int i = 0; i < c.Length; i++)
{
c[i].b = 1.0f;
c[i].a = 1.0f;
}
texture.SetPixels(c, 0);
}
}
}

18
com.unity.render-pipelines.high-definition/HDRP/Editor/Material/StackLit/StackLitUI.cs


protected const string kStencilRefMV = "_StencilRefMV";
protected const string kStencilWriteMaskMV = "_StencilWriteMaskMV";
protected const string k_SpecularAntiAliasingEnabled = "_SpecularAntiAliasingEnabled";
protected const string k_GeometricNormalFilteringEnabled = "_GeometricNormalFilteringEnabled";
protected const string k_TextureNormalFilteringEnabled = "_TextureNormalFilteringEnabled";
#endregion

private Property EnableDualSpecularLobe;
private Property EnableIridescence;
private Property EnableSpecularAA;
private Property EnableGeometricNormalFiltering;
private Property EnableTextureNormalFiltering;
public StackLitGUI()
{

EnableDualSpecularLobe = new Property(this, k_EnableDualSpecularLobe, "Enable Dual Specular Lobe", "Enable a second specular lobe, aim to simulate a mix of a narrow and a haze lobe that better match measured material", true);
EnableIridescence = new Property(this, k_EnableIridescence, "Enable Iridescence", "Enable physically based iridescence layer", true);
EnableSpecularAA = new Property(this, k_SpecularAntiAliasingEnabled, "Enable SpecularAA", "Enable specular antialiasing", true);
EnableGeometricNormalFiltering = new Property(this, k_GeometricNormalFilteringEnabled, "Enable Geometric filtering", "Enable specular antialiasing", true);
EnableTextureNormalFiltering = new Property(this, k_TextureNormalFilteringEnabled, "Enable Texture filtering", "Require normal map to use _NA or _OSNA suffix for normal map name", true);
// All material properties
// All GroupPropery below need to define a

new Property(this, k_AlbedoAffectEmissive, "Albedo Affect Emissive", "Specifies whether or not the emissive color is multiplied by the albedo.", false),
}),
new GroupProperty(this, "_SpecularAntiAliasing", "SpecularAntiAliasing", new BaseProperty[]
new GroupProperty(this, "_SpecularAntiAliasing", "Specular Anti-Aliasing", new BaseProperty[]
EnableSpecularAA,
new Property(this, "_SpecularAntiAliasingScreenSpaceVariance", "Screen Space Variance", "Screen Space Variance (should be less than 0.25)", false, _ => EnableSpecularAA.BoolValue == true),
new Property(this, "_SpecularAntiAliasingThreshold", "Threshold", "Threshold", false, _ => EnableSpecularAA.BoolValue == true),
EnableTextureNormalFiltering,
EnableGeometricNormalFiltering,
new Property(this, "_SpecularAntiAliasingThreshold", "Threshold", "Threshold", false, _ => (EnableGeometricNormalFiltering.BoolValue || EnableTextureNormalFiltering.BoolValue) == true),
new Property(this, "_SpecularAntiAliasingScreenSpaceVariance", "Screen Space Variance", "Screen Space Variance (should be less than 0.25)", false, _ => EnableGeometricNormalFiltering.BoolValue == true),
}),
new GroupProperty(this, "_Debug", "Debug", new BaseProperty[]

2
com.unity.render-pipelines.high-definition/HDRP/Material/LayeredLit/LayeredLitData.hlsl


#ifdef _ENABLE_GEOMETRIC_SPECULAR_AA
// Specular AA
surfaceData.perceptualSmoothness = GeometricFilterPerceptualSmoothness(surfaceData.perceptualSmoothness, input.worldToTangent[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold);
surfaceData.perceptualSmoothness = GeometricNormalFiltering(surfaceData.perceptualSmoothness, input.worldToTangent[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold);
#endif
GetBuiltinData(input, surfaceData, alpha, bentNormalWS, depthOffset, builtinData);

2
com.unity.render-pipelines.high-definition/HDRP/Material/Lit/LitData.hlsl


#ifdef _ENABLE_GEOMETRIC_SPECULAR_AA
// Specular AA
surfaceData.perceptualSmoothness = GeometricFilterPerceptualSmoothness(surfaceData.perceptualSmoothness, input.worldToTangent[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold);
surfaceData.perceptualSmoothness = GeometricNormalFiltering(surfaceData.perceptualSmoothness, input.worldToTangent[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold);
#endif
// Caution: surfaceData must be fully initialize before calling GetBuiltinData

3
com.unity.render-pipelines.high-definition/HDRP/Material/StackLit/StackLit.shader


[HideInInspector] _ZTestDepthEqualForOpaque("_ZTestDepthEqualForOpaque", Int) = 4 // Less equal
[HideInInspector] _ZTestModeDistortion("_ZTestModeDistortion", Int) = 8
[ToggleUI] _SpecularAntiAliasingEnabled("Specular Anti Aliasing Enabled", Float) = 0.0
[ToggleUI] _GeometricNormalFilteringEnabled("GeometricNormalFilteringEnabled", Float) = 0.0
[ToggleUI] _TextureNormalFilteringEnabled("TextureNormalFilteringEnabled", Float) = 0.0
_SpecularAntiAliasingScreenSpaceVariance("SpecularAntiAliasingScreenSpaceVariance", Range(0.0, 1.0)) = 0.1
_SpecularAntiAliasingThreshold("SpecularAntiAliasingThreshold", Range(0.0, 1.0)) = 0.2

50
com.unity.render-pipelines.high-definition/HDRP/Material/StackLit/StackLitData.hlsl


// We forbid scale in case of object space as it make no sense
// Decompress normal ourselve
float3 normalOS = SampleTexture2DTriplanarScaleBias(TEXTURE2D_PARAM(textureName, samplerName), textureNameUV, textureNameUVLocal, textureNameST, uvMapping).xyz * 2.0 - 1.0;
float4 packedNormal = SampleTexture2DTriplanarScaleBias(TEXTURE2D_PARAM(textureName, samplerName), textureNameUV, textureNameUVLocal, textureNameST, uvMapping);
float3 normalOS = packedNormal.xyz * 2.0 - 1.0;
float averageNormalLength = packedNormal.w; // If we used a object space normal map that store average normal, the formap is RGB (normal xyz) and A (average normal length)
return float4(SurfaceGradientFromPerturbedNormal(uvMapping.vertexNormalWS, TransformObjectToWorldDir(normalOS)), 1.0);
return float4(SurfaceGradientFromPerturbedNormal(uvMapping.vertexNormalWS, TransformObjectToWorldDir(normalOS)), averageNormalLength);
}
else
{

float2 derivYPlane;
float2 derivZPlane;
derivXplane = derivYPlane = derivZPlane = float2(0.0, 0.0);
float averageNormalLength = 0.0;
derivXplane = uvMapping.triplanarWeights[textureNameUVLocal].x * UnpackDerivativeNormalRGorAG(SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), TEXCOORD_INDEX_PLANAR_YZ, textureNameUVLocal, textureNameST, uvMapping), scale);
{
float4 packedNormal = SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), TEXCOORD_INDEX_PLANAR_YZ, textureNameUVLocal, textureNameST, uvMapping);
averageNormalLength += uvMapping.triplanarWeights[textureNameUVLocal].x * packedNormal.z;
derivXplane = uvMapping.triplanarWeights[textureNameUVLocal].x * UnpackDerivativeNormalRGorAG(packedNormal, scale);
}
derivYPlane = uvMapping.triplanarWeights[textureNameUVLocal].y * UnpackDerivativeNormalRGorAG(SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), TEXCOORD_INDEX_PLANAR_ZX, textureNameUVLocal, textureNameST, uvMapping), scale);
{
float4 packedNormal = SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), TEXCOORD_INDEX_PLANAR_ZX, textureNameUVLocal, textureNameST, uvMapping);
averageNormalLength += uvMapping.triplanarWeights[textureNameUVLocal].y * packedNormal.z;
derivYPlane = uvMapping.triplanarWeights[textureNameUVLocal].y * UnpackDerivativeNormalRGorAG(packedNormal, scale);
}
derivZPlane = uvMapping.triplanarWeights[textureNameUVLocal].z * UnpackDerivativeNormalRGorAG(SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), TEXCOORD_INDEX_PLANAR_XY, textureNameUVLocal, textureNameST, uvMapping), scale);
{
float4 packedNormal = SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), TEXCOORD_INDEX_PLANAR_XY, textureNameUVLocal, textureNameST, uvMapping);
averageNormalLength += uvMapping.triplanarWeights[textureNameUVLocal].z * packedNormal.z;
derivZPlane = uvMapping.triplanarWeights[textureNameUVLocal].z * UnpackDerivativeNormalRGorAG(packedNormal, scale);
}
return float4(SurfaceGradientFromVolumeGradient(uvMapping.vertexNormalWS, volumeGrad), 1.0);
return float4(SurfaceGradientFromVolumeGradient(uvMapping.vertexNormalWS, volumeGrad), averageNormalLength);
float2 deriv = UnpackDerivativeNormalRGorAG(SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), textureNameUV, textureNameUVLocal, textureNameST, uvMapping), scale);
float4 packedNormal = SampleTexture2DScaleBias(TEXTURE2D_PARAM(textureName, samplerName), textureNameUV, textureNameUVLocal, textureNameST, uvMapping);
float averageNormalLength = packedNormal.z; // If we used a tangent space normal map that store average normal, the formap is RG (normal xy) and B (average normal length)
float2 deriv = UnpackDerivativeNormalRGorAG(packedNormal, scale);
return float4(SurfaceGradientFromTBN(deriv, uvMapping.vertexTangentWS[textureNameUV], uvMapping.vertexBitangentWS[textureNameUV]), 1.0);
return float4(SurfaceGradientFromTBN(deriv, uvMapping.vertexTangentWS[textureNameUV], uvMapping.vertexBitangentWS[textureNameUV]), averageNormalLength);
}
else
{

else if (textureNameUV == TEXCOORD_INDEX_PLANAR_XY)
volumeGrad = float3(deriv.x, deriv.y, 0.0);
return float4(SurfaceGradientFromVolumeGradient(uvMapping.vertexNormalWS, volumeGrad), 1.0f);
return float4(SurfaceGradientFromVolumeGradient(uvMapping.vertexNormalWS, volumeGrad), averageNormalLength);
}
}
}

surfaceData.normalWS = SurfaceGradientResolveNormal(input.worldToTangent[2], gradient.xyz);
surfaceData.coatNormalWS = SurfaceGradientResolveNormal(input.worldToTangent[2], coatGradient.xyz);
if (_SpecularAntiAliasingEnabled > 0.0)
if ((_GeometricNormalFilteringEnabled + _TextureNormalFilteringEnabled) > 0.0)
float geometricVariance = GeometricFilterVariance(input.worldToTangent[2], _SpecularAntiAliasingScreenSpaceVariance);
float textureFilteringVariance = TextureFilteringVariance(gradient.w);
float coatTextureFilteringVariance = TextureFilteringVariance(coatGradient.w);
float geometricVariance = _GeometricNormalFilteringEnabled ? GeometricNormalVariance(input.worldToTangent[2], _SpecularAntiAliasingScreenSpaceVariance) : 0.0;
// gradient.w is the average normal length
float textureFilteringVariance = _TextureNormalFilteringEnabled ? TextureNormalVariance(gradient.w) : 0.0;
float coatTextureFilteringVariance = _TextureNormalFilteringEnabled ? TextureNormalVariance(coatGradient.w) : 0.0;
surfaceData.perceptualSmoothnessA = FilterPerceptualSmoothness(surfaceData.perceptualSmoothnessA, geometricVariance + textureFilteringVariance, _SpecularAntiAliasingThreshold);
surfaceData.perceptualSmoothnessB = FilterPerceptualSmoothness(surfaceData.perceptualSmoothnessB, geometricVariance + textureFilteringVariance, _SpecularAntiAliasingThreshold);
surfaceData.coatPerceptualSmoothness = FilterPerceptualSmoothness(surfaceData.coatPerceptualSmoothness, geometricVariance + coatTextureFilteringVariance, _SpecularAntiAliasingThreshold);
surfaceData.perceptualSmoothnessA = NormalFiltering(surfaceData.perceptualSmoothnessA, geometricVariance + textureFilteringVariance, _SpecularAntiAliasingThreshold);
surfaceData.perceptualSmoothnessB = NormalFiltering(surfaceData.perceptualSmoothnessB, geometricVariance + textureFilteringVariance, _SpecularAntiAliasingThreshold);
surfaceData.coatPerceptualSmoothness = NormalFiltering(surfaceData.coatPerceptualSmoothness, geometricVariance + coatTextureFilteringVariance, _SpecularAntiAliasingThreshold);
}
// TODO: decal etc.

3
com.unity.render-pipelines.high-definition/HDRP/Material/StackLit/StackLitProperties.hlsl


float _EmissiveIntensity;
float _AlbedoAffectEmissive;
float _SpecularAntiAliasingEnabled;
float _GeometricNormalFilteringEnabled;
float _TextureNormalFilteringEnabled;
float _SpecularAntiAliasingScreenSpaceVariance;
float _SpecularAntiAliasingThreshold;

正在加载...
取消
保存