Terrain detail material blending (#5714)

* Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* - Moved settings related to the detail material to a partial view srg owned by the terrain gem.
- Added support for base color in detail materials.
- Hooked up basic base color rendering of detail materials.
- Corrected the way the material data was stored.
- Added ref counting for detail materials so they can be released when no longer used.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Added buffer for material properties of detail mateirals, storing them in a multi-indexed data vector. Updated shader with relevant struct and buffer, but the buffer will need to be moved out of the mateiral SRG to work.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* - Moved settings related to the detail material to a partial view srg owned by the terrain gem.
- Added support for base color in detail materials.
- Hooked up basic base color rendering of detail materials.
- Corrected the way the material data was stored.
- Added ref counting for detail materials so they can be released when no longer used.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Detail materials now put textures into bindless array that's accessed in the shader. Shader now pulls all the detail materal information for a single mateiral but does no blending.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Correcting rebase merge problem.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fix detail roughness fade out with distance.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Adding tests for new MultiIndexedDataVector functions

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Updates to move bindless array to separate SRG
- Exposed BindSrg() in renderpass so it's possible to add additional SRGs to a pass
- Created a TerrainSrg for use by the terrain forward shader
- Moved the bindless array out of the partial view SRG to the TerrainSrg

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Moved more properties out of the view srg to the terrain srg.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Spelling fixes

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fixing bug where the roughness min/max value were inverted. Also fixed bug where bad data would show for areas where there was no macro material.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Detail material blending WIP. Mostly working, but small seams between each materila id pixel.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Switching to using Load() for the detail material IDs and calculating the positions manually since Gather()'s precsion leaves seams along the edges.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Updates from PR review

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fixing case issues and updating function name due to a recent fix.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Switching to using SampleGrad() instead of Sample() for detail textures to fix a bug where the incorrect mip level was chosen around the seams of the detail material id texture.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Remove unneeded sampler and some debug settings in the shader. Condensing some duplicate code

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Updates from PR review and some minor improvements

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Updated with PR feedback. Fixed a fairly significant bug with blending. Also contains a few minor fixes, simplifications, and comments for clarity.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Update weight adjustment equation to trust the compiler less

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* fix bug

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Fixing bug in terrain normal factor. Adjusting normal calculation to avoid the need for an identity transform.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>

* Removing unused fields from the material SRG. Adding basecolor back in in the material type. Updating the default terrain material to not use fields that no longer exist.

Signed-off-by: Ken Pruiksma <pruiksma@amazon.com>
This commit is contained in:
Ken Pruiksma
2021-11-23 17:07:29 -06:00
committed by GitHub
parent 4da6dd812b
commit bbdf871a13
7 changed files with 178 additions and 63 deletions
@@ -2,5 +2,10 @@
"description": "",
"materialType": "PbrTerrain.materialtype",
"parentMaterial": "",
"propertyLayoutVersion": 1
"propertyLayoutVersion": 1,
"properties": {
"baseColor": {
"color": [ 0.18, 0.18, 0.18 ]
}
}
}
@@ -88,6 +88,19 @@
}
}
],
"baseColor": [
{
"name": "color",
"displayName": "Color",
"description": "Color is displayed as sRGB but the values are stored as linear color.",
"type": "Color",
"defaultValue": [ 1.0, 1.0, 1.0 ],
"connection": {
"type": "ShaderInput",
"name": "m_baseColor"
}
}
],
"settings": [
{
"id": "detailTextureMultiplier",
@@ -93,10 +93,6 @@ ShaderResourceGroup ObjectSrg : SRG_PerObject
ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
{
float m_detailTextureMultiplier;
float m_detailFadeDistance;
float m_detailFadeLength;
Sampler m_sampler
{
AddressU = Wrap;
@@ -109,22 +105,11 @@ ShaderResourceGroup TerrainMaterialSrg : SRG_PerMaterial
// Base Color
float3 m_baseColor;
float m_baseColorFactor;
Texture2D m_baseColorMap;
// Normal
Texture2D m_normalMap;
bool m_flipNormalX;
bool m_flipNormalY;
float m_normalFactor;
// Roughness
Texture2D m_roughnessMap;
float m_roughnessFactor;
// Specular
Texture2D m_specularF0Map;
float m_specularF0Factor;
// Detail Material Properties
float m_detailTextureMultiplier;
float m_detailFadeDistance;
float m_detailFadeLength;
}
option bool o_useTerrainSmoothing = false;
@@ -57,7 +57,30 @@ DetailSurface GetDefaultDetailSurface()
return surface;
}
void WeightDetailSurface(inout DetailSurface surface, in float weight)
{
surface.m_color *= weight;
surface.m_normal *= weight;
surface.m_roughness *= weight;
surface.m_specularF0 *= weight;
surface.m_metalness *= weight;
surface.m_occlusion *= weight;
surface.m_height *= weight;
}
void AddDetailSurface(inout DetailSurface surface, in DetailSurface surfaceToAdd)
{
surface.m_color += surfaceToAdd.m_color;
surface.m_normal += surfaceToAdd.m_normal;
surface.m_roughness += surfaceToAdd.m_roughness;
surface.m_specularF0 += surfaceToAdd.m_specularF0;
surface.m_metalness += surfaceToAdd.m_metalness;
surface.m_occlusion += surfaceToAdd.m_occlusion;
surface.m_height += surfaceToAdd.m_height;
}
// Detail material index getters
uint GetDetailColorIndex(TerrainSrg::DetailMaterialData materialData)
{
return materialData.m_colorNormalImageIndices & 0x0000FFFF;
@@ -95,22 +118,22 @@ uint GetDetailHeightIndex(TerrainSrg::DetailMaterialData materialData)
// Detail material value getters
float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv)
float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float3 color = materialData.m_baseColor;
if ((materialData.m_flags & DetailTextureFlags::UseTextureBaseColor) > 0)
{
color = TerrainSrg::m_detailTextures[GetDetailColorIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).rgb;
color = TerrainSrg::m_detailTextures[GetDetailColorIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rgb;
}
return color * materialData.m_baseColorFactor;
}
float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv)
float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float2 normal = float2(0.0, 0.0);
if ((materialData.m_flags & DetailTextureFlags::UseTextureNormal) > 0)
{
normal = TerrainSrg::m_detailTextures[GetDetailNormalIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).rg;
normal = TerrainSrg::m_detailTextures[GetDetailNormalIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rg;
}
// X and Y are inverted here to be consistent with SampleNormalXY in NormalInput.azsli.
@@ -125,53 +148,53 @@ float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv)
return GetTangentSpaceNormal(normal, materialData.m_normalFactor);
}
float GetDetailRoughness(TerrainSrg::DetailMaterialData materialData, float2 uv)
float GetDetailRoughness(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float roughness = materialData.m_roughnessScale;
if ((materialData.m_flags & DetailTextureFlags::UseTextureRoughness) > 0)
{
roughness = TerrainSrg::m_detailTextures[GetDetailRoughnessIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r;
roughness = TerrainSrg::m_detailTextures[GetDetailRoughnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
roughness = materialData.m_roughnessBias + roughness * materialData.m_roughnessScale;
}
return roughness;
}
float GetDetailMetalness(TerrainSrg::DetailMaterialData materialData, float2 uv)
float GetDetailMetalness(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float metalness = 1.0;
if ((materialData.m_flags & DetailTextureFlags::UseTextureMetallic) > 0)
{
metalness = TerrainSrg::m_detailTextures[GetDetailMetalnessIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r;
metalness = TerrainSrg::m_detailTextures[GetDetailMetalnessIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
}
return metalness * materialData.m_metalFactor;
}
float GetDetailSpecularF0(TerrainSrg::DetailMaterialData materialData, float2 uv)
float GetDetailSpecularF0(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float specularF0 = 1.0;
if ((materialData.m_flags & DetailTextureFlags::UseTextureSpecularF0) > 0)
{
specularF0 = TerrainSrg::m_detailTextures[GetDetailSpecularF0Index(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r;
specularF0 = TerrainSrg::m_detailTextures[GetDetailSpecularF0Index(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
}
return specularF0 * materialData.m_specularF0Factor;
}
float GetDetailOcclusion(TerrainSrg::DetailMaterialData materialData, float2 uv)
float GetDetailOcclusion(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float occlusion = 1.0;
if ((materialData.m_flags & DetailTextureFlags::UseTextureOcclusion) > 0)
{
occlusion = TerrainSrg::m_detailTextures[GetDetailOcclusionIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r;
occlusion = TerrainSrg::m_detailTextures[GetDetailOcclusionIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
}
return occlusion * materialData.m_occlusionFactor;
}
float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv)
float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy)
{
float height = materialData.m_heightFactor;
if ((materialData.m_flags & DetailTextureFlags::UseTextureHeight) > 0)
{
height = TerrainSrg::m_detailTextures[GetDetailHeightIndex(materialData)].Sample(TerrainMaterialSrg::m_sampler, uv).r;
height = TerrainSrg::m_detailTextures[GetDetailHeightIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).r;
height = materialData.m_heightOffset + height * materialData.m_heightFactor;
}
return height;
@@ -181,15 +204,19 @@ void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, f
{
TerrainSrg::DetailMaterialData detailMaterialData = TerrainSrg::m_detailMaterialData[materialId];
surface.m_color = GetDetailColor(detailMaterialData, uv);
surface.m_normal = GetDetailNormal(detailMaterialData, uv);
surface.m_roughness = GetDetailRoughness(detailMaterialData, uv);
surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, uv);
surface.m_metalness = GetDetailMetalness(detailMaterialData, uv);
surface.m_occlusion = GetDetailOcclusion(detailMaterialData, uv);
surface.m_height = GetDetailHeight(detailMaterialData, uv);
float2 uvDdx = ddx(uv);
float2 uvDdy = ddy(uv);
surface.m_color = GetDetailColor(detailMaterialData, uv, uvDdx, uvDdy);
surface.m_normal = GetDetailNormal(detailMaterialData, uv, uvDdx, uvDdy);
surface.m_roughness = GetDetailRoughness(detailMaterialData, uv, uvDdx, uvDdy);
surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, uv, uvDdx, uvDdy);
surface.m_metalness = GetDetailMetalness(detailMaterialData, uv, uvDdx, uvDdy);
surface.m_occlusion = GetDetailOcclusion(detailMaterialData, uv, uvDdx, uvDdy);
surface.m_height = GetDetailHeight(detailMaterialData, uv, uvDdx, uvDdy);
}
// Debugs the detail material by choosing a random color per material ID and rendering it without blending.
void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint material2, float blend, float2 idUv)
{
float3 material1Color = float3(0.1, 0.1, 0.1);
@@ -210,7 +237,7 @@ void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint mat
surface.m_color = lerp(material1Color, material2Color, blend);
float seamBlend = 0.0;
const float halfLineWidth = 1.0 / 2048.0;
if (any(abs(idUv) % 1.0 < halfLineWidth) || any(abs(idUv) % 1.0 > 1.0 - halfLineWidth))
if (any(frac(abs(idUv)) < halfLineWidth) || any(frac(abs(idUv)) > 1.0 - halfLineWidth))
{
seamBlend = 1.0;
}
@@ -225,26 +252,114 @@ void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint mat
surface.m_height = 0.5;
}
bool GetDetailSurface(inout DetailSurface surface, float2 idUv, float2 uv)
//Blend a single detail material sample (with two possible material ids) onto a DetailSurface.
void BlendDetailMaterial(inout DetailSurface surface, uint material1, uint material2, float blend, float2 detailUv, float weight)
{
uint4 material1 = TerrainSrg::m_detailMaterialIdImage.GatherRed(TerrainSrg::DetailSampler, idUv, 0).xyzw;
uint4 material2 = TerrainSrg::m_detailMaterialIdImage.GatherGreen(TerrainSrg::DetailSampler, idUv, 0).xyzw;
DetailSurface tempSurface;
GetDetailSurfaceForMaterial(tempSurface, material1, detailUv);
WeightDetailSurface(tempSurface, weight * (1.0 - blend));
AddDetailSurface(surface, tempSurface);
if (material2 != 0xFF)
{
GetDetailSurfaceForMaterial(tempSurface, material2, detailUv);
WeightDetailSurface(tempSurface, weight * blend);
AddDetailSurface(surface, tempSurface);
}
}
const float maxBlendAmount = 0xFF;
/*
Populates a DetailSurface with material data gathered form the 4 nearest samples to detailMaterialIdUv. The weight
of each detail material's contribution is calculated based on the distance to the center point for that sample (for
instance, if detailMaterialIdUv falls perfectly in-between all 4 samples, then each sample will be weighed at 25%).
Each sample can have two different detail materials defined with a blend value to determine their relative contribution.
The detailUv is used for sampling the textures of each detail material.
*/
bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdUv, float2 detailUv)
{
float2 textureSize;
TerrainSrg::m_detailMaterialIdImage.GetDimensions(textureSize.x, textureSize.y);
float2 detailMaterialIdCoord = detailMaterialIdUv * textureSize; // uv -> pixel coordinate
// detailMaterialIdCoord could be negative, so add textureSize to ensure it is positive
detailMaterialIdCoord += textureSize;
// The detail material id texture wraps since the "center" point can be anywhere in the texture, so mod by texturesize
int2 detailMaterailIdTopLeft = int2(detailMaterialIdCoord) % textureSize;
int2 detailMaterailIdBottomRight = (int2(detailMaterialIdCoord) + 1) % textureSize;
// Using Load() to gather the nearest 4 samples (Gather4() isn't used because of precision issues with uvs).
uint4 s1 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdTopLeft.x, detailMaterailIdBottomRight.y, 0));
uint4 s2 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdBottomRight, 0));
uint4 s3 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdBottomRight.x, detailMaterailIdTopLeft.y, 0));
uint4 s4 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterailIdTopLeft, 0));
uint4 material1 = uint4(s1.x, s2.x, s3.x, s4.x);
uint4 material2 = uint4(s1.y, s2.y, s3.y, s4.y);
// convert integer of 0-255 to float of 0-1.
float4 blends = float4(TerrainSrg::m_detailMaterialIdImage.GatherBlue(TerrainSrg::DetailSampler, idUv, 0).xyzw) / maxBlendAmount;
const float maxBlendAmount = 0xFF;
float4 blends = float4(s1.z, s2.z, s3.z, s4.z) / maxBlendAmount;
// Calculate weight based on proximity to detail material samples
float2 gatherWeight = frac(detailMaterialIdCoord);
// Adjust the gather weight for better interpolation by (3x^2 - 2x^3). This helps avoid diamond-shaped artifacts in binlinear filtering.
gatherWeight = gatherWeight * gatherWeight * (3.0 - 2.0 * gatherWeight);
if (o_debugDetailMaterialIds)
{
float2 idUv = (detailMaterialIdCoord + gatherWeight - 0.5) / textureSize;
GetDebugDetailSurface(surface, material1.x, material2.x, blends.x, idUv);
return true;
}
if (material1.x == 0xFF)
// If any sample has no materials, give up.
if (any(material1 == 0xFF))
{
return false;
}
GetDetailSurfaceForMaterial(surface, material1.x, uv);
if (all(material1.x == material1.yzw) && all(material2.x == material2.yzw))
{
// Fast path for same material ids
GetDetailSurfaceForMaterial(surface, material1.x, detailUv);
if (material2.x != 0xFF)
{
float4 material2Blends = 1.0 - blends;
DetailSurface tempSurface;
float weight =
((1.0 - gatherWeight.x) * gatherWeight.y * material2Blends.x) +
(gatherWeight.x * gatherWeight.y * material2Blends.y) +
(gatherWeight.x * (1.0 - gatherWeight.y) * material2Blends.z) +
((1.0 - gatherWeight.x) * (1.0 - gatherWeight.y) * material2Blends.w);
WeightDetailSurface(surface, weight);
GetDetailSurfaceForMaterial(tempSurface, material2.x, detailUv);
WeightDetailSurface(tempSurface, 1.0 - weight);
AddDetailSurface(surface, tempSurface);
}
}
else
{
surface = (DetailSurface)0;
// X
float weight = (1.0 - gatherWeight.x) * gatherWeight.y;
BlendDetailMaterial(surface, material1.x, material2.x, blends.x, detailUv, weight);
// Y
weight = gatherWeight.x * gatherWeight.y;
BlendDetailMaterial(surface, material1.y, material2.y, blends.y, detailUv, weight);
// Z
weight = gatherWeight.x * (1.0 - gatherWeight.y);
BlendDetailMaterial(surface, material1.z, material2.z, blends.z, detailUv, weight);
// W
weight = (1.0 - gatherWeight.x) * (1.0 - gatherWeight.y);
BlendDetailMaterial(surface, material1.w, material2.w, blends.w, detailUv, weight);
}
surface.m_normal = normalize(surface.m_normal);
return true;
}
@@ -109,9 +109,10 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
{
bool flipX = ObjectSrg::m_macroMaterialData[i].m_flipNormalX;
bool flipY = ObjectSrg::m_macroMaterialData[i].m_flipNormalY;
bool factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor;
macroNormal = GetNormalInputTS(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler,
macroUv, flipX, flipY, CreateIdentity3x3(), true, factor);
float factor = ObjectSrg::m_macroMaterialData[i].m_normalFactor;
float2 sampledValue = SampleNormalXY(ObjectSrg::m_macroNormalMap[i], TerrainMaterialSrg::m_sampler, macroUv, flipX, flipY);
macroNormal = normalize(GetTangentSpaceNormal_Unnormalized(sampledValue.xy, factor));
}
break;
}
@@ -129,7 +130,7 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN)
// Check to make sure we're inside the detail texture's bounds and within where detail textures should be drawn.
if (detailFactor < 1.0 && all(detailRegionUv > TerrainSrg::m_detailHalfPixelUv) && all(detailRegionUv < 1.0 - TerrainSrg::m_detailHalfPixelUv))
{
detailRegionUv += TerrainSrg::m_detailMaterialIdImageCenter - (0.5);
detailRegionUv += TerrainSrg::m_detailMaterialIdImageCenter - 0.5;
hasDetailSurface = GetDetailSurface(detailSurface, detailRegionUv, detailUv);
}
@@ -1,6 +1,12 @@
{
"Source" : "./TerrainPBR_ForwardPass.azsl",
"CompilerHints" :
{
"DisableOptimizations" : false,
"GenerateDebugInfo" : false
},
"DepthStencilState" :
{
"Depth" :
@@ -17,16 +17,6 @@ ShaderResourceGroupSemantic SRG_Terrain
ShaderResourceGroup TerrainSrg : SRG_Terrain
{
Sampler DetailSampler
{
AddressU = Wrap;
AddressV = Wrap;
MinFilter = Point;
MagFilter = Point;
MipFilter = Point;
};
struct DetailMaterialData
{
// Uv