您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
220 行
10 KiB
220 行
10 KiB
// Share by Lit and LayeredLit. Return object scaling for displacement map depends if it is vertex (affect vertex displacement) or pixel displacement (affect tiling)
|
|
float3 GetDisplacementObjectScale(bool vertexDisplacement)
|
|
{
|
|
float3 objectScale = float3(1.0, 1.0, 1.0);
|
|
|
|
// TODO: This should be an uniform for the object, this code should be remove once we have it. - Workaround for now
|
|
// To handle object scaling with pixel displacement we need to multiply the view vector by the inverse scale.
|
|
// To Handle object scaling with vertex/tessellation displacement we must multiply displacement by object scale
|
|
// Currently we extract either the scale (ObjectToWorld) or the inverse scale (worldToObject) directly by taking the transform matrix
|
|
float4x4 worldTransform;
|
|
if (vertexDisplacement)
|
|
{
|
|
worldTransform = GetObjectToWorldMatrix();
|
|
}
|
|
|
|
else
|
|
{
|
|
worldTransform = GetWorldToObjectMatrix();
|
|
}
|
|
|
|
objectScale.x = length(float3(worldTransform._m00, worldTransform._m01, worldTransform._m02));
|
|
// In the specific case of pixel displacement mapping, to get a consistent behavior compare to tessellation we require to not take into account y scale if lock object scale is not enabled
|
|
#if !defined(_PIXEL_DISPLACEMENT) || (defined(_PIXEL_DISPLACEMENT_LOCK_OBJECT_SCALE))
|
|
objectScale.y = length(float3(worldTransform._m10, worldTransform._m11, worldTransform._m12));
|
|
#endif
|
|
objectScale.z = length(float3(worldTransform._m20, worldTransform._m21, worldTransform._m22));
|
|
|
|
return objectScale;
|
|
}
|
|
|
|
#ifndef LAYERED_LIT_SHADER
|
|
|
|
// Note: This function is call by both Per vertex and Per pixel displacement
|
|
float GetMaxDisplacement()
|
|
{
|
|
float maxDisplacement = 0.0;
|
|
#if defined(_HEIGHTMAP)
|
|
maxDisplacement = abs(_HeightAmplitude); // _HeightAmplitude can be negative if min and max are inverted, but the max displacement must be positive
|
|
#endif
|
|
return maxDisplacement;
|
|
}
|
|
|
|
// Return the minimun uv size for all layers including triplanar
|
|
float2 GetMinUvSize(LayerTexCoord layerTexCoord)
|
|
{
|
|
float2 minUvSize = float2(FLT_MAX, FLT_MAX);
|
|
|
|
#if defined(_HEIGHTMAP)
|
|
if (layerTexCoord.base.mappingType == UV_MAPPING_TRIPLANAR)
|
|
{
|
|
minUvSize = min(layerTexCoord.base.uvZY * _HeightMap_TexelSize.zw, minUvSize);
|
|
minUvSize = min(layerTexCoord.base.uvXZ * _HeightMap_TexelSize.zw, minUvSize);
|
|
minUvSize = min(layerTexCoord.base.uvXY * _HeightMap_TexelSize.zw, minUvSize);
|
|
}
|
|
else
|
|
{
|
|
minUvSize = min(layerTexCoord.base.uv * _HeightMap_TexelSize.zw, minUvSize);
|
|
}
|
|
#endif
|
|
|
|
return minUvSize;
|
|
}
|
|
|
|
struct PerPixelHeightDisplacementParam
|
|
{
|
|
float2 uv;
|
|
};
|
|
|
|
float ComputePerPixelHeightDisplacement(float2 texOffsetCurrent, float lod, PerPixelHeightDisplacementParam param)
|
|
{
|
|
// Note: No multiply by amplitude here. This is include in the maxHeight provide to POM
|
|
// Tiling is automatically handled correctly here.
|
|
return SAMPLE_TEXTURE2D_LOD(_HeightMap, sampler_HeightMap, param.uv + texOffsetCurrent, lod).r;
|
|
}
|
|
|
|
#include "ShaderLibrary/PerPixelDisplacement.hlsl"
|
|
|
|
void ApplyDisplacementTileScale(inout float height)
|
|
{
|
|
// Inverse tiling scale = 2 / (abs(_BaseColorMap_ST.x) + abs(_BaseColorMap_ST.y)
|
|
// Inverse tiling scale *= (1 / _TexWorldScale) if planar or triplanar
|
|
#ifdef _DISPLACEMENT_LOCK_TILING_SCALE
|
|
height *= _InvTilingScale;
|
|
#endif
|
|
}
|
|
|
|
float ApplyPerPixelDisplacement(FragInputs input, float3 V, inout LayerTexCoord layerTexCoord)
|
|
{
|
|
#if defined(_PIXEL_DISPLACEMENT) && defined(_HEIGHTMAP)
|
|
// These variables are known at the compile time.
|
|
bool isPlanar = layerTexCoord.base.mappingType == UV_MAPPING_PLANAR;
|
|
bool isTriplanar = layerTexCoord.base.mappingType == UV_MAPPING_TRIPLANAR;
|
|
|
|
// See comment in layered version for details
|
|
float maxHeight = GetMaxDisplacement();
|
|
ApplyDisplacementTileScale(maxHeight);
|
|
float2 minUvSize = GetMinUvSize(layerTexCoord);
|
|
float lod = ComputeTextureLOD(minUvSize);
|
|
|
|
// TODO: precompute uvSpaceScale
|
|
float2 invPrimScale = (isPlanar || isTriplanar) ? float2(1.0, 1.0) : _InvPrimScale.xy;
|
|
float worldScale = (isPlanar || isTriplanar) ? _TexWorldScale : 1.0;
|
|
float2 uvSpaceScale = invPrimScale * _BaseColorMap_ST.xy * (worldScale * maxHeight);
|
|
float2 scaleOffsetDetails = _DetailMap_ST.xy;
|
|
|
|
PerPixelHeightDisplacementParam ppdParam;
|
|
|
|
float height = 0; // final height processed
|
|
float NdotV = 0;
|
|
|
|
// planar/triplanar
|
|
float2 uvXZ;
|
|
float2 uvXY;
|
|
float2 uvZY;
|
|
GetTriplanarCoordinate(V, uvXZ, uvXY, uvZY);
|
|
|
|
// TODO: support object space planar/triplanar ?
|
|
|
|
// We need to calculate the texture space direction. It depends on the mapping.
|
|
if (isTriplanar)
|
|
{
|
|
float planeHeight;
|
|
|
|
// Perform a POM in each direction and modify appropriate texture coordinate
|
|
[branch] if (layerTexCoord.triplanarWeights.x >= 0.001)
|
|
{
|
|
ppdParam.uv = layerTexCoord.base.uvZY;
|
|
float3 viewDirTS = float3(uvZY, abs(V.x));
|
|
float3 viewDirUV = normalize(float3(viewDirTS.xy * uvSpaceScale, viewDirTS.z)); // TODO: skip normalize
|
|
float unitAngle = saturate(FastACosPos(viewDirUV.z) * INV_HALF_PI); // TODO: optimize
|
|
int numSteps = (int)lerp(_PPDMinSamples, _PPDMaxSamples, unitAngle);
|
|
float2 offset = ParallaxOcclusionMapping(lod, _PPDLodThreshold, numSteps, viewDirUV, ppdParam, planeHeight);
|
|
|
|
// Apply offset to all triplanar UVSet
|
|
layerTexCoord.base.uvZY += offset;
|
|
layerTexCoord.details.uvZY += offset * scaleOffsetDetails;
|
|
height += layerTexCoord.triplanarWeights.x * planeHeight;
|
|
NdotV += layerTexCoord.triplanarWeights.x * viewDirTS.z;
|
|
}
|
|
|
|
[branch] if (layerTexCoord.triplanarWeights.y >= 0.001)
|
|
{
|
|
ppdParam.uv = layerTexCoord.base.uvXZ;
|
|
float3 viewDirTS = float3(uvXZ, abs(V.y));
|
|
float3 viewDirUV = normalize(float3(viewDirTS.xy * uvSpaceScale, viewDirTS.z)); // TODO: skip normalize
|
|
float unitAngle = saturate(FastACosPos(viewDirUV.z) * INV_HALF_PI); // TODO: optimize
|
|
int numSteps = (int)lerp(_PPDMinSamples, _PPDMaxSamples, unitAngle);
|
|
float2 offset = ParallaxOcclusionMapping(lod, _PPDLodThreshold, numSteps, viewDirUV, ppdParam, planeHeight);
|
|
|
|
layerTexCoord.base.uvXZ += offset;
|
|
layerTexCoord.details.uvXZ += offset * scaleOffsetDetails;
|
|
height += layerTexCoord.triplanarWeights.y * planeHeight;
|
|
NdotV += layerTexCoord.triplanarWeights.y * viewDirTS.z;
|
|
}
|
|
|
|
[branch] if (layerTexCoord.triplanarWeights.z >= 0.001)
|
|
{
|
|
ppdParam.uv = layerTexCoord.base.uvXY;
|
|
float3 viewDirTS = float3(uvXY, abs(V.z));
|
|
float3 viewDirUV = normalize(float3(viewDirTS.xy * uvSpaceScale, viewDirTS.z)); // TODO: skip normalize
|
|
float unitAngle = saturate(FastACosPos(viewDirUV.z) * INV_HALF_PI); // TODO: optimize
|
|
int numSteps = (int)lerp(_PPDMinSamples, _PPDMaxSamples, unitAngle);
|
|
float2 offset = ParallaxOcclusionMapping(lod, _PPDLodThreshold, numSteps, viewDirUV, ppdParam, planeHeight);
|
|
|
|
layerTexCoord.base.uvXY += offset;
|
|
layerTexCoord.details.uvXY += offset * scaleOffsetDetails;
|
|
height += layerTexCoord.triplanarWeights.z * planeHeight;
|
|
NdotV += layerTexCoord.triplanarWeights.z * viewDirTS.z;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ppdParam.uv = layerTexCoord.base.uv; // For planar it is uv too, not uvXZ
|
|
|
|
// Note: The TBN is not normalize as it is based on mikkt. We should normalize it, but POM is always use on simple enough surfarce that mean it is not required (save 2 normalize). Tag: SURFACE_GRADIENT
|
|
float3 viewDirTS = isPlanar ? float3(uvXZ, V.y) : TransformWorldToTangent(V, input.worldToTangent) * GetDisplacementObjectScale(false).xzy; // Switch from Y-up to Z-up (as we move to tangent space)
|
|
NdotV = viewDirTS.z;
|
|
|
|
// Transform the view vector into the UV space.
|
|
float3 viewDirUV = normalize(float3(viewDirTS.xy * uvSpaceScale, viewDirTS.z)); // TODO: skip normalize
|
|
float unitAngle = saturate(FastACosPos(viewDirUV.z) * INV_HALF_PI); // TODO: optimize
|
|
int numSteps = (int)lerp(_PPDMinSamples, _PPDMaxSamples, unitAngle);
|
|
float2 offset = ParallaxOcclusionMapping(lod, _PPDLodThreshold, numSteps, viewDirUV, ppdParam, height);
|
|
|
|
// Apply offset to all UVSet0 / planar
|
|
layerTexCoord.base.uv += offset;
|
|
// Note: Applying offset on detail uv is only correct if it use the same UVSet or is planar or triplanar. It is up to the user to do the correct thing.
|
|
layerTexCoord.details.uv += offset * scaleOffsetDetails;
|
|
}
|
|
|
|
// Since POM "pushes" geometry inwards (rather than extrude it), { height = height - 1 }.
|
|
// Since the result is used as a 'depthOffsetVS', it needs to be positive, so we flip the sign.
|
|
float verticalDisplacement = maxHeight - height * maxHeight;
|
|
return verticalDisplacement / max(NdotV, 0.001);
|
|
#else
|
|
return 0.0;
|
|
#endif
|
|
}
|
|
|
|
// Calculate displacement for per vertex displacement mapping
|
|
float3 ComputePerVertexDisplacement(LayerTexCoord layerTexCoord, float4 vertexColor, float lod)
|
|
{
|
|
float height = (SAMPLE_UVMAPPING_TEXTURE2D_LOD(_HeightMap, sampler_HeightMap, layerTexCoord.base, lod).r - _HeightCenter) * _HeightAmplitude;
|
|
|
|
// Height is affected by tiling property and by object scale (depends on option).
|
|
// Apply scaling from tiling properties (TexWorldScale and tiling from BaseColor)
|
|
ApplyDisplacementTileScale(height);
|
|
// Applying scaling of the object if requested
|
|
#ifdef _VERTEX_DISPLACEMENT_LOCK_OBJECT_SCALE
|
|
float3 objectScale = GetDisplacementObjectScale(true);
|
|
// Reminder: mappingType is know statically, so code below is optimize by the compiler
|
|
// Planar and Triplanar are in world space thus it is independent of object scale
|
|
return height.xxx * ((layerTexCoord.base.mappingType == UV_MAPPING_UVSET) ? objectScale : float3(1.0, 1.0, 1.0));
|
|
#else
|
|
return height.xxx;
|
|
#endif
|
|
}
|
|
|
|
#endif // #ifndef LAYERED_LIT_SHADER
|
|
|