您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
378 行
16 KiB
378 行
16 KiB
void ADD_IDX(ComputeLayerTexCoord)( float2 texCoord0, float2 texCoord1, float2 texCoord2, float2 texCoord3,
|
|
float3 positionWS, float3 normalWS, bool isTriplanar, inout LayerTexCoord layerTexCoord, float additionalTiling = 1.0)
|
|
{
|
|
// Handle uv0, uv1, uv2, uv3 based on _UVMappingMask weight (exclusif 0..1)
|
|
float2 uvBase = ADD_IDX(_UVMappingMask).x * texCoord0 +
|
|
ADD_IDX(_UVMappingMask).y * texCoord1 +
|
|
ADD_IDX(_UVMappingMask).z * texCoord2 +
|
|
ADD_IDX(_UVMappingMask).w * texCoord3;
|
|
|
|
uvBase *= additionalTiling.xx;
|
|
|
|
|
|
float2 uvDetails = ADD_IDX(_UVDetailsMappingMask).x * texCoord0 +
|
|
ADD_IDX(_UVDetailsMappingMask).y * texCoord1 +
|
|
ADD_IDX(_UVDetailsMappingMask).z * texCoord2 +
|
|
ADD_IDX(_UVDetailsMappingMask).w * texCoord3;
|
|
|
|
// Note that if base is planar/triplanar, detail map is too
|
|
|
|
// planar
|
|
// TODO: Do we want to manage local or world triplanar/planar
|
|
//float3 position = localTriplanar ? TransformWorldToObject(positionWS) : positionWS;
|
|
float3 position = positionWS;
|
|
position *= ADD_IDX(_TexWorldScale);
|
|
|
|
if (ADD_IDX(_UVMappingPlanar) > 0.0)
|
|
{
|
|
uvBase = -position.xz;
|
|
uvDetails = -position.xz;
|
|
}
|
|
|
|
ADD_IDX(layerTexCoord.base).uv = TRANSFORM_TEX(uvBase, ADD_IDX(_BaseColorMap));
|
|
ADD_IDX(layerTexCoord.details).uv = TRANSFORM_TEX(uvDetails, ADD_IDX(_DetailMap));
|
|
|
|
// triplanar
|
|
ADD_IDX(layerTexCoord.base).isTriplanar = isTriplanar;
|
|
|
|
float3 direction = sign(normalWS);
|
|
|
|
// In triplanar, if we are facing away from the world axis, a different axis will be flipped for each direction.
|
|
// This is particularly problematic for tangent space normal maps which need to be in the right direction.
|
|
// So we multiplying the offending coordinate by the sign of the normal.
|
|
float2 uvYZ = float2(direction.x * position.z, position.y);
|
|
float2 uvZX = -float2(position.x, direction.y * position.z);
|
|
float2 uvXY = float2(-position.x, direction.z * position.y);
|
|
|
|
ADD_IDX(layerTexCoord.base).uvYZ = TRANSFORM_TEX(uvYZ, ADD_IDX(_BaseColorMap));
|
|
ADD_IDX(layerTexCoord.base).uvZX = TRANSFORM_TEX(uvZX, ADD_IDX(_BaseColorMap));
|
|
ADD_IDX(layerTexCoord.base).uvXY = TRANSFORM_TEX(uvXY, ADD_IDX(_BaseColorMap));
|
|
|
|
ADD_IDX(layerTexCoord.details).isTriplanar = isTriplanar;
|
|
|
|
ADD_IDX(layerTexCoord.details).uvYZ = TRANSFORM_TEX(uvYZ, ADD_IDX(_DetailMap));
|
|
ADD_IDX(layerTexCoord.details).uvZX = TRANSFORM_TEX(uvZX, ADD_IDX(_DetailMap));
|
|
ADD_IDX(layerTexCoord.details).uvXY = TRANSFORM_TEX(uvXY, ADD_IDX(_DetailMap));
|
|
}
|
|
|
|
float ADD_IDX(SampleHeightmap)(LayerTexCoord layerTexCoord, float centerOffset = 0.0, float multiplier = 1.0)
|
|
{
|
|
#ifdef _HEIGHTMAP
|
|
return (SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), ADD_IDX(layerTexCoord.base)).r - ADD_IDX(_HeightCenter) - centerOffset) * ADD_IDX(_HeightAmplitude) * multiplier;
|
|
#else
|
|
return 0.0;
|
|
#endif
|
|
}
|
|
|
|
float ADD_IDX(SampleHeightmapLod)(LayerTexCoord layerTexCoord, float lod, float centerOffset = 0.0, float multiplier = 1.0)
|
|
{
|
|
#ifdef _HEIGHTMAP
|
|
return (SAMPLE_LAYER_TEXTURE2D_LOD(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), ADD_IDX(layerTexCoord.base), lod).r - ADD_IDX(_HeightCenter) - centerOffset) * ADD_IDX(_HeightAmplitude) * multiplier;
|
|
#else
|
|
return 0.0;
|
|
#endif
|
|
}
|
|
|
|
// Note: The sampling of heightmap inside POM don't use sampling abstraction (with triplanar) as
|
|
// POM must be apply separately for each uv set (so 3 time for triplanar)
|
|
void ADD_IDX(ParallaxOcclusionMappingLayer)(inout LayerTexCoord layerTexCoord, int numSteps, float3 viewDirTS)
|
|
{
|
|
// Convention: 1.0 is top, 0.0 is bottom - POM is always inward, no extrusion
|
|
float stepSize = 1.0 / (float)numSteps;
|
|
|
|
// View vector is from the point to the camera, but we want to raymarch from camera to point, so reverse the sign
|
|
// The length of viewDirTS vector determines the furthest amount of displacement:
|
|
// float parallaxLimit = -length(viewDirTS.xy) / viewDirTS.z;
|
|
// float2 parallaxDir = normalize(Out.viewDirTS.xy);
|
|
// float2 parallaxMaxOffsetTS = parallaxDir * parallaxLimit;
|
|
// Above code simplify to
|
|
float2 parallaxMaxOffsetTS = (viewDirTS.xy / -viewDirTS.z) * ADD_IDX(_HeightAmplitude);
|
|
float2 texOffsetPerStep = stepSize * parallaxMaxOffsetTS;
|
|
|
|
float2 uv = ADD_IDX(layerTexCoord.base).uv;
|
|
|
|
// Compute lod as we will sample inside a loop (so can't use regular sampling)
|
|
// It appear that CALCULATE_TEXTURE2D_LOD only return interger lod. We want to use float lod to have smoother transition and fading
|
|
// float lod = CALCULATE_TEXTURE2D_LOD(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), uv);
|
|
float lod = ComputeTextureLOD(uv, GET_TEXELSIZE_NAME(ADD_IDX(_HeightMap)));
|
|
|
|
// Do a first step before the loop to init all value correctly
|
|
float2 texOffsetCurrent = 0;
|
|
float prevHeight = SAMPLE_TEXTURE2D_LOD(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), uv + texOffsetCurrent, lod).r;
|
|
texOffsetCurrent += texOffsetPerStep;
|
|
float currHeight = SAMPLE_TEXTURE2D_LOD(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), uv + texOffsetCurrent, lod).r;
|
|
float rayHeight = 1.0 - stepSize; // Start at top less one sample
|
|
|
|
// Linear search
|
|
for (int stepIndex = 0; stepIndex < numSteps; ++stepIndex)
|
|
{
|
|
// Have we found a height below our ray height ? then we have an intersection
|
|
if (currHeight > rayHeight)
|
|
break; // end the loop
|
|
|
|
prevHeight = currHeight;
|
|
rayHeight -= stepSize;
|
|
texOffsetCurrent += texOffsetPerStep;
|
|
|
|
// Sample height map which in this case is stored in the alpha channel of the normal map:
|
|
currHeight = SAMPLE_TEXTURE2D_LOD(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), uv + texOffsetCurrent, lod).r;
|
|
}
|
|
|
|
// Found below and above points, now perform line interesection (ray) with piecewise linear heightfield approximation
|
|
|
|
// Refine the search by adding few extra intersection
|
|
#define POM_REFINE 1
|
|
#if POM_REFINE
|
|
|
|
float pt0 = rayHeight + stepSize;
|
|
float pt1 = rayHeight;
|
|
float delta0 = pt0 - prevHeight;
|
|
float delta1 = pt1 - currHeight;
|
|
|
|
float2 offset = float2(0.0, 0.0);
|
|
|
|
float threshold = 1.0;
|
|
|
|
for (int i = 0; i < 5; ++i)
|
|
{
|
|
float t = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0);
|
|
offset = (1 - t) * texOffsetPerStep * numSteps;
|
|
|
|
currHeight = SAMPLE_TEXTURE2D_LOD(ADD_IDX(_HeightMap), ADD_ZERO_IDX(sampler_HeightMap), uv + offset, lod).r;
|
|
|
|
threshold = t - currHeight;
|
|
|
|
if (abs(threshold) <= 0.01)
|
|
break;
|
|
|
|
if (threshold < 0.0)
|
|
{
|
|
delta1 = threshold;
|
|
pt1 = t;
|
|
}
|
|
else
|
|
{
|
|
delta0 = threshold;
|
|
pt0 = t;
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
//float pt0 = rayHeight + stepSize;
|
|
//float pt1 = rayHeight;
|
|
//float delta0 = pt0 - prevHeight;
|
|
//float delta1 = pt1 - currHeight;
|
|
//float t = (pt0 * delta1 - pt1 * delta0) / (delta1 - delta0);
|
|
//float2 offset = (1 - t) * texOffsetPerStep * numSteps;
|
|
|
|
// A bit more optimize
|
|
float delta0 = currHeight - rayHeight;
|
|
float delta1 = (rayHeight + stepSize) - prevHeight;
|
|
float ratio = delta0 / (delta0 + delta1);
|
|
float2 offset = texOffsetCurrent - ratio * texOffsetPerStep;
|
|
|
|
#endif
|
|
|
|
// TODO: expose LOD fading
|
|
//float lodThreshold = 0.0;
|
|
//offset *= (1.0 - saturate(lod - lodThreshold));
|
|
|
|
// Apply offset only on base. Details could use another mapping and will not be consistant...
|
|
// Don't know if this will still ok.
|
|
// TODO: check with artists
|
|
ADD_IDX(layerTexCoord.base).uv += offset;
|
|
}
|
|
|
|
float3 ADD_IDX(GetNormalTS)(FragInputs input, LayerTexCoord layerTexCoord, float3 detailNormalTS, float detailMask, bool useBias, float bias)
|
|
{
|
|
float3 normalTS;
|
|
|
|
#ifdef _NORMALMAP
|
|
#ifdef _NORMALMAP_TANGENT_SPACE
|
|
if (useBias)
|
|
{
|
|
normalTS = SAMPLE_LAYER_NORMALMAP_BIAS(ADD_IDX(_NormalMap), ADD_ZERO_IDX(sampler_NormalMap), ADD_IDX(layerTexCoord.base), ADD_IDX(_NormalScale), bias);
|
|
}
|
|
else
|
|
{
|
|
normalTS = SAMPLE_LAYER_NORMALMAP(ADD_IDX(_NormalMap), ADD_ZERO_IDX(sampler_NormalMap), ADD_IDX(layerTexCoord.base), ADD_IDX(_NormalScale));
|
|
}
|
|
#else // Object space
|
|
// to be able to combine object space normal with detail map we transform it to tangent space (object space normal composition is not simple).
|
|
// then later we will re-transform it to world space.
|
|
if (useBias)
|
|
{
|
|
float3 normalOS = SAMPLE_LAYER_NORMALMAP_RGB_BIAS(ADD_IDX(_NormalMap), ADD_ZERO_IDX(sampler_NormalMap), ADD_IDX(layerTexCoord.base), ADD_IDX(_NormalScale), bias).rgb;
|
|
normalTS = TransformObjectToTangent(normalOS, input.tangentToWorld);
|
|
}
|
|
else
|
|
{
|
|
float3 normalOS = SAMPLE_LAYER_NORMALMAP_RGB(ADD_IDX(_NormalMap), ADD_ZERO_IDX(sampler_NormalMap), ADD_IDX(layerTexCoord.base), ADD_IDX(_NormalScale)).rgb;
|
|
normalTS = TransformObjectToTangent(normalOS, input.tangentToWorld);
|
|
}
|
|
#endif
|
|
|
|
#ifdef _DETAIL_MAP
|
|
normalTS = lerp(normalTS, BlendNormalRNM(normalTS, detailNormalTS), detailMask);
|
|
#endif
|
|
#else
|
|
normalTS = float3(0.0, 0.0, 1.0);
|
|
#endif
|
|
|
|
#if defined(_DOUBLESIDED_LIGHTING_FLIP) || defined(_DOUBLESIDED_LIGHTING_MIRROR)
|
|
#ifdef _DOUBLESIDED_LIGHTING_FLIP
|
|
float3 oppositeNormalTS = -normalTS;
|
|
#else
|
|
// Mirror the normal with the plane define by vertex normal
|
|
float3 oppositeNormalTS = reflect(normalTS, float3(0.0, 0.0, 1.0)); // Reflect around vertex normal (in tangent space this is z)
|
|
#endif
|
|
// TODO : Test if GetOddNegativeScale() is necessary here in case of normal map, as GetOddNegativeScale is take into account in CreateTangentToWorld();
|
|
normalTS = input.isFrontFace ?
|
|
(GetOddNegativeScale() >= 0.0 ? normalTS : oppositeNormalTS) :
|
|
(-GetOddNegativeScale() >= 0.0 ? normalTS : oppositeNormalTS);
|
|
#endif
|
|
|
|
return normalTS;
|
|
}
|
|
|
|
// Return opacity
|
|
float ADD_IDX(GetSurfaceData)(FragInputs input, LayerTexCoord layerTexCoord, out SurfaceData surfaceData, out float3 normalTS)
|
|
{
|
|
#ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
|
|
float alpha = ADD_IDX(_BaseColor).a;
|
|
#else
|
|
float alpha = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_BaseColorMap), ADD_ZERO_IDX(sampler_BaseColorMap), ADD_IDX(layerTexCoord.base)).a * ADD_IDX(_BaseColor).a;
|
|
#endif
|
|
|
|
// Perform alha test very early to save performance (a killed pixel will not sample textures)
|
|
#if defined(_ALPHATEST_ON) && !defined(LAYERED_LIT_SHADER)
|
|
clip(alpha - _AlphaCutoff);
|
|
#endif
|
|
|
|
float3 detailNormalTS = float3(0.0, 0.0, 0.0);
|
|
float detailMask = 0.0;
|
|
#ifdef _DETAIL_MAP
|
|
detailMask = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_DetailMask), ADD_ZERO_IDX(sampler_DetailMask), ADD_IDX(layerTexCoord.base)).g;
|
|
float2 detailAlbedoAndSmoothness = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_DetailMap), ADD_ZERO_IDX(sampler_DetailMap), ADD_IDX(layerTexCoord.details)).rb;
|
|
float detailAlbedo = detailAlbedoAndSmoothness.r;
|
|
float detailSmoothness = detailAlbedoAndSmoothness.g;
|
|
#ifdef _DETAIL_MAP_WITH_NORMAL
|
|
// Resample the detail map but this time for the normal map. This call should be optimize by the compiler
|
|
// We split both call due to trilinear mapping
|
|
detailNormalTS = SAMPLE_LAYER_NORMALMAP_AG(ADD_IDX(_DetailMap), ADD_ZERO_IDX(sampler_DetailMap), ADD_IDX(layerTexCoord.details), ADD_ZERO_IDX(_DetailNormalScale));
|
|
//float detailAO = 0.0;
|
|
#else
|
|
// TODO: Use heightmap as a derivative with Morten Mikklesen approach, how this work with our abstraction and triplanar ?
|
|
detailNormalTS = float3(0.0, 0.0, 1.0);
|
|
//float detailAO = detail.b;
|
|
#endif
|
|
#endif
|
|
|
|
surfaceData.baseColor = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_BaseColorMap), ADD_ZERO_IDX(sampler_BaseColorMap), ADD_IDX(layerTexCoord.base)).rgb * ADD_IDX(_BaseColor).rgb;
|
|
#ifdef _DETAIL_MAP
|
|
surfaceData.baseColor *= LerpWhiteTo(2.0 * saturate(detailAlbedo * ADD_IDX(_DetailAlbedoScale)), detailMask);
|
|
#endif
|
|
|
|
#ifdef _SPECULAROCCLUSIONMAP
|
|
// TODO: Do something. For now just take alpha channel
|
|
surfaceData.specularOcclusion = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_SpecularOcclusionMap), ADD_ZERO_IDX(sampler_SpecularOcclusionMap), ADD_IDX(layerTexCoord.base)).a;
|
|
#else
|
|
// The specular occlusion will be perform outside the internal loop
|
|
surfaceData.specularOcclusion = 1.0;
|
|
#endif
|
|
surfaceData.normalWS = float3(0.0, 0.0, 0.0); // Need to init this so that the compiler leaves us alone.
|
|
|
|
// TODO: think about using BC5
|
|
normalTS = ADD_IDX(GetNormalTS)(input, layerTexCoord, detailNormalTS, detailMask, false, 0.0);
|
|
|
|
#ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
|
|
surfaceData.perceptualSmoothness = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_BaseColorMap), ADD_ZERO_IDX(sampler_BaseColorMap), ADD_IDX(layerTexCoord.base)).a;
|
|
#elif defined(_MASKMAP)
|
|
surfaceData.perceptualSmoothness = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_MaskMap), ADD_ZERO_IDX(sampler_MaskMap), ADD_IDX(layerTexCoord.base)).a;
|
|
#else
|
|
surfaceData.perceptualSmoothness = 1.0;
|
|
#endif
|
|
surfaceData.perceptualSmoothness *= ADD_IDX(_Smoothness);
|
|
#ifdef _DETAIL_MAP
|
|
surfaceData.perceptualSmoothness *= LerpWhiteTo(2.0 * saturate(detailSmoothness * ADD_IDX(_DetailSmoothnessScale)), detailMask);
|
|
#endif
|
|
|
|
// MaskMap is Metallic, Ambient Occlusion, (Optional) - emissive Mask, Optional - Smoothness (in alpha)
|
|
#ifdef _MASKMAP
|
|
surfaceData.metallic = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_MaskMap), ADD_ZERO_IDX(sampler_MaskMap), ADD_IDX(layerTexCoord.base)).r;
|
|
surfaceData.ambientOcclusion = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_MaskMap), ADD_ZERO_IDX(sampler_MaskMap), ADD_IDX(layerTexCoord.base)).g;
|
|
#else
|
|
surfaceData.metallic = 1.0;
|
|
surfaceData.ambientOcclusion = 1.0;
|
|
#endif
|
|
surfaceData.metallic *= ADD_IDX(_Metallic);
|
|
|
|
// This part of the code is not used in case of layered shader but we keep the same macro system for simplicity
|
|
#if !defined(LAYERED_LIT_SHADER)
|
|
|
|
surfaceData.materialId = _MaterialID;
|
|
|
|
// TODO: think about using BC5
|
|
#ifdef _TANGENTMAP
|
|
#ifdef _NORMALMAP_TANGENT_SPACE // Normal and tangent use same space
|
|
float3 tangentTS = SAMPLE_LAYER_NORMALMAP(ADD_IDX(_TangentMap), ADD_ZERO_IDX(sampler_TangentMap), ADD_IDX(layerTexCoord.base), 1.0);
|
|
surfaceData.tangentWS = TransformTangentToWorld(tangentTS, input.tangentToWorld);
|
|
#else // Object space
|
|
float3 tangentOS = SAMPLE_LAYER_NORMALMAP_RGB(ADD_IDX(_TangentMap), ADD_ZERO_IDX(sampler_TangentMap), ADD_IDX(layerTexCoord.base), 1.0).rgb;
|
|
surfaceData.tangentWS = TransformObjectToWorldDir(tangentOS);
|
|
#endif
|
|
#else
|
|
surfaceData.tangentWS = normalize(input.tangentToWorld[0].xyz);
|
|
#endif
|
|
// TODO: Is there anything todo regarding flip normal but for the tangent ?
|
|
|
|
#ifdef _ANISOTROPYMAP
|
|
surfaceData.anisotropy = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_AnisotropyMap), ADD_ZERO_IDX(sampler_AnisotropyMap), ADD_IDX(layerTexCoord.base)).b;
|
|
#else
|
|
surfaceData.anisotropy = 1.0;
|
|
#endif
|
|
surfaceData.anisotropy *= ADD_IDX(_Anisotropy);
|
|
|
|
surfaceData.specular = 0.04;
|
|
|
|
surfaceData.subsurfaceProfile = _SubsurfaceProfile;
|
|
#ifdef _Subsurface_RADIUS_MAP
|
|
surfaceData.subsurfaceProfile = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_SubsurfaceRadiusMap), ADD_ZERO_IDX(sampler_SubsurfaceRadiusMap), ADD_IDX(layerTexCoord.base)).r;
|
|
#else
|
|
surfaceData.subsurfaceRadius = _SubsurfaceRadius;
|
|
#endif
|
|
#ifdef _THICKNESS_MAP
|
|
surfaceData.thickness = SAMPLE_LAYER_TEXTURE2D(ADD_IDX(_ThicknessMap), ADD_ZERO_IDX(sampler_ThicknessMap), ADD_IDX(layerTexCoord.base)).r;
|
|
#else
|
|
surfaceData.thickness = _Thickness;
|
|
#endif
|
|
|
|
surfaceData.coatNormalWS = float3(1.0, 0.0, 0.0);
|
|
surfaceData.coatPerceptualSmoothness = 1.0;
|
|
surfaceData.specularColor = float3(0.0, 0.0, 0.0);
|
|
|
|
#else // #if !defined(LAYERED_LIT_SHADER)
|
|
|
|
// Mandatory to setup value to keep compiler quiet
|
|
|
|
// Layered shader only support materialId 0
|
|
surfaceData.materialId = 0;
|
|
|
|
surfaceData.tangentWS = input.tangentToWorld[0].xyz;
|
|
surfaceData.anisotropy = 0;
|
|
surfaceData.specular = 0.04;
|
|
|
|
surfaceData.subsurfaceRadius = 1.0;
|
|
surfaceData.thickness = 0.0;
|
|
surfaceData.subsurfaceProfile = 0;
|
|
|
|
surfaceData.coatNormalWS = float3(1.0, 0.0, 0.0);
|
|
surfaceData.coatPerceptualSmoothness = 1.0;
|
|
surfaceData.specularColor = float3(0.0, 0.0, 0.0);
|
|
|
|
#endif // #if !defined(LAYERED_LIT_SHADER)
|
|
|
|
return alpha;
|
|
}
|
|
|