Frag
void Frag(PackedVaryingsToPS packedInput,
#ifdef OUTPUT_SPLIT_LIGHTING
out float4 outColor : SV_Target0, // outSpecularLighting
out float4 outDiffuseLighting : SV_Target1,
OUTPUT_SSSBUFFER(outSSSBuffer)
#else
out float4 outColor : SV_Target0
#endif
#ifdef _DEPTHOFFSET_ON
, out float outputDepth : SV_Depth
#endif
)
{
FragInputs input = UnpackVaryingsMeshToFragInputs(packedInput.vmesh);
uint2 tileIndex = uint2(input.positionSS.xy) / GetTileSize();
#if defined(UNITY_SINGLE_PASS_STEREO)
tileIndex.x -= unity_StereoEyeIndex * _NumTileClusteredX;
#endif
// input.positionSS is SV_Position
PositionInputs posInput = GetPositionInput_Stereo(input.positionSS.xy, _ScreenSize.zw, input.positionSS.z, input.positionSS.w, input.positionRWS.xyz, tileIndex, unity_StereoEyeIndex);
#ifdef VARYINGS_NEED_POSITION_WS
float3 V = GetWorldSpaceNormalizeViewDir(input.positionRWS);
#else
// Unused
float3 V = float3(1.0, 1.0, 1.0); // Avoid the division by 0
#endif
SurfaceData surfaceData;
BuiltinData builtinData;
GetSurfaceAndBuiltinData(input, V, posInput, surfaceData, builtinData);
BSDFData bsdfData = ConvertSurfaceDataToBSDFData(input.positionSS.xy, surfaceData);
PreLightData preLightData = GetPreLightData(V, posInput, bsdfData);
outColor = float4(0.0, 0.0, 0.0, 0.0);
// We need to skip lighting when doing debug pass because the debug pass is done before lighting so some buffers may not be properly initialized potentially causing crashes on PS4.
#ifdef DEBUG_DISPLAY
// Init in debug display mode to quiet warning
#ifdef OUTPUT_SPLIT_LIGHTING
outDiffuseLighting = 0;
ENCODE_INTO_SSSBUFFER(surfaceData, posInput.positionSS, outSSSBuffer);
#endif
// Same code in ShaderPassForwardUnlit.shader
if (_DebugViewMaterial != 0)
{
float3 result = float3(1.0, 0.0, 1.0);
bool needLinearToSRGB = false;
GetPropertiesDataDebug(_DebugViewMaterial, result, needLinearToSRGB);
GetVaryingsDataDebug(_DebugViewMaterial, input, result, needLinearToSRGB);
GetBuiltinDataDebug(_DebugViewMaterial, builtinData, result, needLinearToSRGB);
GetSurfaceDataDebug(_DebugViewMaterial, surfaceData, result, needLinearToSRGB);
GetBSDFDataDebug(_DebugViewMaterial, bsdfData, result, needLinearToSRGB);
// TEMP!
// For now, the final blit in the backbuffer performs an sRGB write
// So in the meantime we apply the inverse transform to linear data to compensate.
if (!needLinearToSRGB)
result = SRGBToLinear(max(0, result));
outColor = float4(result, 1.0);
}
else
#endif
{
#ifdef _SURFACE_TYPE_TRANSPARENT
uint featureFlags = LIGHT_FEATURE_MASK_FLAGS_TRANSPARENT;
#else
uint featureFlags = LIGHT_FEATURE_MASK_FLAGS_OPAQUE;
#endif
float3 diffuseLighting;
float3 specularLighting;
LightLoop(V, posInput, preLightData, bsdfData, builtinData, featureFlags, diffuseLighting, specularLighting);
#ifdef OUTPUT_SPLIT_LIGHTING
if (_EnableSubsurfaceScattering != 0 && ShouldOutputSplitLighting(bsdfData))
{
outColor = float4(specularLighting, 1.0);
outDiffuseLighting = float4(TagLightingForSSS(diffuseLighting), 1.0);
}
else
{
outColor = float4(diffuseLighting + specularLighting, 1.0);
outDiffuseLighting = 0;
}
ENCODE_INTO_SSSBUFFER(surfaceData, posInput.positionSS, outSSSBuffer);
#else
outColor = ApplyBlendMode(diffuseLighting, specularLighting, builtinData.opacity);
outColor = EvaluateAtmosphericScattering(posInput, V, outColor);
#endif
}
#ifdef _DEPTHOFFSET_ON
outputDepth = posInput.deviceDepth;
#endif
}
输入和输出
先看输入和输出,输入就是从Vert得到的PackedVaryingsToPS,如果定义OUTPUT_SPLIT_LIGHTING输出是SpecularLighting,DiffuseLighting和一个宏,不是则是Color。同时定义_DEPTHOFFSET_ON时再输出一张depth。
// If we use subsurface scattering, enable output split lighting (for forward pass)
#if defined(_MATERIAL_FEATURE_SUBSURFACE_SCATTERING) && !defined(_SURFACE_TYPE_TRANSPARENT)
#define OUTPUT_SPLIT_LIGHTING
#endif
#define OUTPUT_SSSBUFFER(NAME) out SSSBufferType0 MERGE_NAME(NAME, 0) : SV_Target2
分别在Lit.shader和SubsurfaceScattering.hlsl中定义。
暂时也不考虑SSS,所以这部分之后也会略过。
转化Input数据
FragInputs input = UnpackVaryingsMeshToFragInputs(packedInput.vmesh);
struct FragInputs
{
// Contain value return by SV_POSITION (That is name positionCS in PackedVarying).
// xy: unormalized screen position (offset by 0.5), z: device depth, w: depth in view space
// Note: SV_POSITION is the result of the clip space position provide to the vertex shaders that is transform by the viewport
float4 positionSS; // In case depth offset is use, positionRWS.w is equal to depth offset
float3 positionRWS; // Relative camera space position
float4 texCoord0;
float4 texCoord1;
float4 texCoord2;
float4 texCoord3;
float4 color; // vertex color
// TODO: confirm with Morten following statement
// Our TBN is orthogonal but is maybe not orthonormal in order to be compliant with external bakers (Like xnormal that use mikktspace).
// (xnormal for example take into account the interpolation when baking the normal and normalizing the tangent basis could cause distortion).
// When using worldToTangent with surface gradient, it doesn't normalize the tangent/bitangent vector (We instead use exact same scale as applied to interpolated vertex normal to avoid breaking compliance).
// this mean that any usage of worldToTangent[1] or worldToTangent[2] outside of the context of normal map (like for POM) must normalize the TBN (TCHECK if this make any difference ?)
// When not using surface gradient, each vector of worldToTangent are normalize (TODO: Maybe they should not even in case of no surface gradient ? Ask Morten)
float3x3 worldToTangent;
// For two sided lighting
bool isFrontFace;
};
定义在FragInputs.hlsl,注释写的很详细。
FragInputs UnpackVaryingsMeshToFragInputs(PackedVaryingsMeshToPS input)
{
FragInputs output;
ZERO_INITIALIZE(FragInputs, output);
UNITY_SETUP_INSTANCE_ID(input);
// Init to some default value to make the computer quiet (else it output "divide by zero" warning even if value is not used).
// TODO: this is a really poor workaround, but the variable is used in a bunch of places
// to compute normals which are then passed on elsewhere to compute other values...
output.worldToTangent = k_identity3x3;
output.positionSS = input.positionCS; // input.positionCS is SV_Position
#ifdef VARYINGS_NEED_POSITION_WS
output.positionRWS.xyz = input.interpolators0.xyz;
#endif
#ifdef VARYINGS_NEED_TANGENT_TO_WORLD
float4 tangentWS = float4(input.interpolators2.xyz, input.interpolators2.w > 0.0 ? 1.0 : -1.0); // must not be normalized (mikkts requirement)
output.worldToTangent = BuildWorldToTangent(tangentWS, input.interpolators1.xyz);
#endif // VARYINGS_NEED_TANGENT_TO_WORLD
#ifdef VARYINGS_NEED_TEXCOORD0
output.texCoord0.xy = input.interpolators3.xy;
#endif
#ifdef VARYINGS_NEED_TEXCOORD1
output.texCoord1.xy = input.interpolators3.zw;
#endif
#ifdef VARYINGS_NEED_TEXCOORD2
output.texCoord2.xy = input.interpolators4.xy;
#endif
#ifdef VARYINGS_NEED_TEXCOORD3
output.texCoord3.xy = input.interpolators4.zw;
#endif
#ifdef VARYINGS_NEED_COLOR
output.color = input.interpolators5;
#endif
#if defined(VARYINGS_NEED_CULLFACE) && SHADER_STAGE_FRAGMENT
output.isFrontFace = IS_FRONT_VFACE(input.cullFace, true, false);
#endif
return output;
}
static const float3x3 k_identity3x3 = {1, 0, 0,
0, 1, 0,
0, 0, 1};
UnpackVaryingsMeshToFragInputs定义在VaryingMesh.hlsl,其中k_identity3x3定义在Common.hlsl,是3x3单位矩阵,只要再注意下positionCS的语义是SV_Position,在光栅化后xy转换成屏幕坐标,在这里变量名变为positionSS。
// This function assumes the bitangent flip is encoded in tangentWS.w
float3x3 BuildWorldToTangent(float4 tangentWS, float3 normalWS)
{
// tangentWS must not be normalized (mikkts requirement)
// Normalize normalWS vector but keep the renormFactor to apply it to bitangent and tangent
float3 unnormalizedNormalWS = normalWS;
float renormFactor = 1.0 / length(unnormalizedNormalWS);
// bitangent on the fly option in xnormal to reduce vertex shader outputs.
// this is the mikktspace transformation (must use unnormalized attributes)
float3x3 worldToTangent = CreateWorldToTangent(unnormalizedNormalWS, tangentWS.xyz, tangentWS.w > 0.0 ? 1.0 : -1.0);
// surface gradient based formulation requires a unit length initial normal. We can maintain compliance with mikkts
// by uniformly scaling all 3 vectors since normalization of the perturbed normal will cancel it.
worldToTangent[0] = worldToTangent[0] * renormFactor;
worldToTangent[1] = worldToTangent[1] * renormFactor;
worldToTangent[2] = worldToTangent[2] * renormFactor; // normalizes the interpolated vertex normal
return worldToTangent;
}
BuildWorldToTangent定义在ShaderVariablesFunctions.hlsl,用世界坐标下的切向量,次切向量及法线构建,并且我们的normalWS也存在worldToTangent[2]里了。
CreateWorldToTangent定义在SpaceTransforms.hlsl中,由于篇幅原因,一部分函数以后不再详细写在文章里。
uint2 tileIndex = uint2(input.positionSS.xy) / GetTileSize();
#if defined(UNITY_SINGLE_PASS_STEREO)
tileIndex.x -= unity_StereoEyeIndex * _NumTileClusteredX;
#endif
Unity默认情况下不透明物体用的是FPTL,虽然FPTL效率较高,但半透明因为没有depth的原因用的是Clustered shading,两者原理差不多,都需要先求得tile的id。
UNITY_SINGLE_PASS_STEREO和vr有关,后续也会略过。
PositionInputs posInput = GetPositionInput_Stereo(input.positionSS.xy, _ScreenSize.zw, input.positionSS.z, input.positionSS.w, input.positionRWS.xyz, tileIndex, unity_StereoEyeIndex);
struct PositionInputs
{
float3 positionWS; // World space position (could be camera-relative)
float2 positionNDC; // Normalized screen coordinates within the viewport : [0, 1) (with the half-pixel offset)
uint2 positionSS; // Screen space pixel coordinates : [0, NumPixels)
uint2 tileCoord; // Screen tile coordinates : [0, NumTiles)
float deviceDepth; // Depth from the depth buffer : [0, 1] (typically reversed)
float linearDepth; // View space Z coordinate : [Near, Far]
};
// This function is use to provide an easy way to sample into a screen texture, either from a pixel or a compute shaders.
// This allow to easily share code.
// If a compute shader call this function positionSS is an integer usually calculate like: uint2 positionSS = groupId.xy * BLOCK_SIZE + groupThreadId.xy
// else it is current unormalized screen coordinate like return by SV_Position
PositionInputs GetPositionInput_Stereo(float2 positionSS, float2 invScreenSize, uint2 tileCoord, uint eye) // Specify explicit tile coordinates so that we can easily make it lane invariant for compute evaluation.
{
PositionInputs posInput;
ZERO_INITIALIZE(PositionInputs, posInput);
posInput.positionNDC = positionSS;
#if SHADER_STAGE_COMPUTE
// In case of compute shader an extra half offset is added to the screenPos to shift the integer position to pixel center.
posInput.positionNDC.xy += float2(0.5, 0.5);
#endif
posInput.positionNDC *= invScreenSize;
#if defined(UNITY_SINGLE_PASS_STEREO)
posInput.positionNDC.x = posInput.positionNDC.x - eye;
#endif
posInput.positionSS = uint2(positionSS);
posInput.tileCoord = tileCoord;
return posInput;
}
定义在Common.hlsl。
计算获取材质信息
#ifdef VARYINGS_NEED_POSITION_WS
float3 V = GetWorldSpaceNormalizeViewDir(input.positionRWS);
#else
// Unused
float3 V = float3(1.0, 1.0, 1.0); // Avoid the division by 0
#endif
计算ViewDir,下面的算式是unused。
SurfaceData surfaceData;
BuiltinData builtinData;
GetSurfaceAndBuiltinData(input, V, posInput, surfaceData, builtinData);
我们先不看结构体的定义,定义将放在后面讲,这里不会用到。
void GetSurfaceAndBuiltinData(FragInputs input, float3 V, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group
uint3 fadeMaskSeed = asuint((int3)(V * _ScreenSize.xyx)); // Quantize V to _ScreenSize values
LODDitheringTransition(fadeMaskSeed, unity_LODFade.x);
#endif
#ifdef _DOUBLESIDED_ON
float3 doubleSidedConstants = _DoubleSidedConstants.xyz;
#else
float3 doubleSidedConstants = float3(1.0, 1.0, 1.0);
#endif
ApplyDoubleSidedFlipOrMirror(input, doubleSidedConstants); // Apply double sided flip on the vertex normal
LayerTexCoord layerTexCoord;
ZERO_INITIALIZE(LayerTexCoord, layerTexCoord);
GetLayerTexCoord(input, layerTexCoord);
float depthOffset = ApplyPerPixelDisplacement(input, V, layerTexCoord);
#ifdef _DEPTHOFFSET_ON
ApplyDepthOffsetPositionInput(V, depthOffset, GetViewForwardDir(), GetWorldToHClipMatrix(), posInput);
#endif
// We perform the conversion to world of the normalTS outside of the GetSurfaceData
// so it allow us to correctly deal with detail normal map and optimize the code for the layered shaders
float3 normalTS;
float3 bentNormalTS;
float3 bentNormalWS;
float alpha = GetSurfaceData(input, layerTexCoord, surfaceData, normalTS, bentNormalTS);
GetNormalWS(input, normalTS, surfaceData.normalWS, doubleSidedConstants);
// Use bent normal to sample GI if available
#ifdef _BENTNORMALMAP
GetNormalWS(input, bentNormalTS, bentNormalWS, doubleSidedConstants);
#else
bentNormalWS = surfaceData.normalWS;
#endif
surfaceData.geomNormalWS = input.worldToTangent[2];
// By default we use the ambient occlusion with Tri-ace trick (apply outside) for specular occlusion.
// If user provide bent normal then we process a better term
#if defined(_BENTNORMALMAP) && defined(_ENABLESPECULAROCCLUSION)
// If we have bent normal and ambient occlusion, process a specular occlusion
#ifdef SPECULAR_OCCLUSION_USE_SPTD
surfaceData.specularOcclusion = GetSpecularOcclusionFromBentAOPivot(V, bentNormalWS, surfaceData.normalWS, surfaceData.ambientOcclusion, PerceptualSmoothnessToPerceptualRoughness(surfaceData.perceptualSmoothness));
#else
surfaceData.specularOcclusion = GetSpecularOcclusionFromBentAO(V, bentNormalWS, surfaceData.normalWS, surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
#endif
#elif defined(_MASKMAP)
surfaceData.specularOcclusion = GetSpecularOcclusionFromAmbientOcclusion(ClampNdotV(dot(surfaceData.normalWS, V)), surfaceData.ambientOcclusion, PerceptualSmoothnessToRoughness(surfaceData.perceptualSmoothness));
#else
surfaceData.specularOcclusion = 1.0;
#endif
// This is use with anisotropic material
surfaceData.tangentWS = Orthonormalize(surfaceData.tangentWS, surfaceData.normalWS);
#if HAVE_DECALS
if (_EnableDecals)
{
DecalSurfaceData decalSurfaceData = GetDecalSurfaceData(posInput, alpha);
ApplyDecalToSurfaceData(decalSurfaceData, surfaceData);
}
#endif
#ifdef _ENABLE_GEOMETRIC_SPECULAR_AA
// Specular AA
surfaceData.perceptualSmoothness = GeometricNormalFiltering(surfaceData.perceptualSmoothness, input.worldToTangent[2], _SpecularAAScreenSpaceVariance, _SpecularAAThreshold);
#endif
#if defined(DEBUG_DISPLAY)
if (_DebugMipMapMode != DEBUGMIPMAPMODE_NONE)
{
surfaceData.baseColor = GetTextureDataDebug(_DebugMipMapMode, layerTexCoord.base.uv, _BaseColorMap, _BaseColorMap_TexelSize, _BaseColorMap_MipInfo, surfaceData.baseColor);
surfaceData.metallic = 0;
}
// We need to call ApplyDebugToSurfaceData after filling the surfarcedata and before filling builtinData
// as it can modify attribute use for static lighting
ApplyDebugToSurfaceData(input.worldToTangent, surfaceData);
#endif
// Caution: surfaceData must be fully initialize before calling GetBuiltinData
GetBuiltinData(input, V, posInput, surfaceData, alpha, bentNormalWS, depthOffset, builtinData);
}
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group
uint3 fadeMaskSeed = asuint((int3)(V * _ScreenSize.xyx)); // Quantize V to _ScreenSize values
LODDitheringTransition(fadeMaskSeed, unity_LODFade.x);
#endif
这部分是LOD相关,LODDitheringTransition定义在Common.hlsl,大致就是根据unity_LODFade.x和随机值利用clip镂空来进行LOD之间的过度。
#ifdef _DOUBLESIDED_ON
float3 doubleSidedConstants = _DoubleSidedConstants.xyz;
#else
float3 doubleSidedConstants = float3(1.0, 1.0, 1.0);
#endif
ApplyDoubleSidedFlipOrMirror(input, doubleSidedConstants); // Apply double sided flip on the vertex normal
这是反面法线的模式,由editor控制,可以看BaseLitGUI是怎么写的:
switch (doubleSidedNormalMode)
{
case DoubleSidedNormalMode.Mirror: // Mirror mode (in tangent space)
material.SetVector("_DoubleSidedConstants", new Vector4(1.0f, 1.0f, -1.0f, 0.0f));
break;
case DoubleSidedNormalMode.Flip: // Flip mode (in tangent space)
material.SetVector("_DoubleSidedConstants", new Vector4(-1.0f, -1.0f, -1.0f, 0.0f));
break;
case DoubleSidedNormalMode.None: // None mode (in tangent space)
material.SetVector("_DoubleSidedConstants", new Vector4(1.0f, 1.0f, 1.0f, 0.0f));
break;
}
默认情况是Mirror,但和Flip一样,把normalWS方向翻转,具体见下。
// Flipping or mirroring a normal can be done directly on the tangent space. This has the benefit to apply to the whole process either in surface gradient or not.
// This function will modify FragInputs and this is not propagate outside of GetSurfaceAndBuiltinData(). This is ok as tangent space is not use outside of GetSurfaceAndBuiltinData().
void ApplyDoubleSidedFlipOrMirror(inout FragInputs input, float3 doubleSidedConstants)
{
#ifdef _DOUBLESIDED_ON
// 'doubleSidedConstants' is float3(-1, -1, -1) in flip mode and float3(1, 1, -1) in mirror mode.
// It's float3(1, 1, 1) in the none mode.
float flipSign = input.isFrontFace ? 1.0 : doubleSidedConstants.z;
// For the 'Flip' mode, we should not modify the tangent and the bitangent (which correspond
// to the surface derivatives), and instead modify (invert) the displacements.
input.worldToTangent[2] = flipSign * input.worldToTangent[2]; // normal
#endif
}
定义在MaterialUtilities.hlsl,其中input.worldToTangent[2]就是normalWS,文章上面有写。
LayerTexCoord layerTexCoord;
ZERO_INITIALIZE(LayerTexCoord, layerTexCoord);
GetLayerTexCoord(input, layerTexCoord);
UV相关的代码。
struct LayerTexCoord
{
#ifndef LAYERED_LIT_SHADER
UVMapping base;
UVMapping details;
#else
// Regular texcoord
UVMapping base0;
UVMapping base1;
UVMapping base2;
UVMapping base3;
UVMapping details0;
UVMapping details1;
UVMapping details2;
UVMapping details3;
// Dedicated for blend mask
UVMapping blendMask;
#endif
// Store information that will be share by all UVMapping
float3 vertexNormalWS; // TODO: store also object normal map for object triplanar
float3 triplanarWeights;
#ifdef SURFACE_GRADIENT
// tangent basis for each UVSet - up to 4 for now
float3 vertexTangentWS0, vertexBitangentWS0;
float3 vertexTangentWS1, vertexBitangentWS1;
float3 vertexTangentWS2, vertexBitangentWS2;
float3 vertexTangentWS3, vertexBitangentWS3;
#endif
};
定义在LitData.hlsl。
注意:LAYERED_LIT_SHADER是多层材质,不在文章分析范围内。
struct UVMapping
{
int mappingType;
float2 uv; // Current uv or planar uv
// Triplanar specific
float2 uvZY;
float2 uvXZ;
float2 uvXY;
float3 normalWS; // vertex normal
float3 triplanarWeights;
#ifdef SURFACE_GRADIENT
// tangent basis to use when mappingType is UV_MAPPING_UVSET
// these are vertex level in world space
float3 tangentWS;
float3 bitangentWS;
// TODO: store also object normal map for object triplanar
#endif
};
定义在SampleUVMapping.hlsl。
// This maybe call directly by tessellation (domain) shader, thus all part regarding surface gradient must be done
// in function with FragInputs input as parameters
// layerTexCoord must have been initialize to 0 outside of this function
void GetLayerTexCoord(float2 texCoord0, float2 texCoord1, float2 texCoord2, float2 texCoord3,
float3 positionRWS, float3 vertexNormalWS, inout LayerTexCoord layerTexCoord)
{
layerTexCoord.vertexNormalWS = vertexNormalWS;
layerTexCoord.triplanarWeights = ComputeTriplanarWeights(vertexNormalWS);
int mappingType = UV_MAPPING_UVSET;
#if defined(_MAPPING_PLANAR)
mappingType = UV_MAPPING_PLANAR;
#elif defined(_MAPPING_TRIPLANAR)
mappingType = UV_MAPPING_TRIPLANAR;
#endif
// Be sure that the compiler is aware that we don't use UV1 to UV3 for main layer so it can optimize code
ComputeLayerTexCoord( texCoord0, texCoord1, texCoord2, texCoord3, _UVMappingMask, _UVDetailsMappingMask,
_BaseColorMap_ST.xy, _BaseColorMap_ST.zw, _DetailMap_ST.xy, _DetailMap_ST.zw, 1.0, _LinkDetailsWithBase,
positionRWS, _TexWorldScale,
mappingType, layerTexCoord);
}
// This is call only in this file
// layerTexCoord must have been initialize to 0 outside of this function
void GetLayerTexCoord(FragInputs input, inout LayerTexCoord layerTexCoord)
{
#ifdef SURFACE_GRADIENT
GenerateLayerTexCoordBasisTB(input, layerTexCoord);
#endif
GetLayerTexCoord( input.texCoord0.xy, input.texCoord1.xy, input.texCoord2.xy, input.texCoord3.xy,
input.positionRWS, input.worldToTangent[2].xyz, layerTexCoord);
}
定义在LitData.hlsl,其中GenerateLayerTexCoordBasisTB也定义在LitData.hlsl,作用是第二至四套的UV生成根据UV自生生成它的Tangent空间。
ComputeTriplanarWeights定义在CommonMaterial.hlsl,用于Unity的Triplanar实现。
ComputeLayerTexCoord定义在LitDataIndividualLayer.hlsl,用于base和details生成相应的UVMapping。
float depthOffset = ApplyPerPixelDisplacement(input, V, layerTexCoord);
#ifdef _DEPTHOFFSET_ON
ApplyDepthOffsetPositionInput(V, depthOffset, GetViewForwardDir(), GetWorldToHClipMatrix(), posInput);
#endif
Displacement相关,用高度图变换高度。相关代码没仔细看,可以在LitDataDisplacement.hlsl和Common.hlsl中看详情。
下一篇继续分析GetSurfaceAndBuiltinData函数。
来源:CSDN
作者:Calette
链接:https://blog.csdn.net/Calette/article/details/103600537