publicreadonlyGUIContentheightOffset=newGUIContent("Height Offset","Offset applied to the height before layering.");
publicreadonlyGUIContentheightTransition=newGUIContent("Height Transition","Size in world units of the smooth transition between layers.");
publicreadonlyGUIContentperPixelDisplacementLayersWarning=newGUIContent("For pixel displacement to work correctly, all layers with a heightmap must use the same UV mapping");
[Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3, Planar, 4, Triplanar, 5)] _UVBase0("UV Set for base0", Float) = 0 // no UV1/2/3 for main layer (matching Lit.shader and for PPDisplacement restriction)
[Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3, Planar, 4, Triplanar, 5)] _UVBase1("UV Set for base1", Float) = 0
[Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3, Planar, 4, Triplanar, 5)] _UVBase2("UV Set for base2", Float) = 0
[Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3, Planar, 4, Triplanar, 5)] _UVBase0("UV Set for base0", Float) = 0 // no UV1/2/3 for main layer (matching Lit.shader and for PPDisplacement restriction)
[Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3, Planar, 4, Triplanar, 5)] _UVBase1("UV Set for base1", Float) = 0
[Enum(UV0, 0, UV1, 1, UV2, 2, UV3, 3, Planar, 4, Triplanar, 5)] _UVBase2("UV Set for base2", Float) = 0
publicstaticGUIContentdoubleSidedNormalModeText=newGUIContent("Normal mode","This will modify the normal base on the selected mode. None: untouch, Mirror: Mirror the normal with vertex normal plane, Flip: Flip the normal");
publicstaticGUIContentdepthOffsetEnableText=newGUIContent("Enable Depth Offset","EnableDepthOffset on this shader (Use with heightmap)");
// Displacement mapping (POM, tessellation, per vertex)
//public static GUIContent enablePerPixelDisplacementText = new GUIContent("Enable Per Pixel Displacement", "");
publicstaticGUIContentdisplacementModeText=newGUIContent("Displacement mode","Apply heightmap displacement to the selected element: Vertex, pixel or tessellated vertex. Pixel displacement must be use with flat surfaces, it is an expensive features and typical usage is paved road.");
publicstaticGUIContentlockWithObjectScaleText=newGUIContent("Lock with object scale","Displacement mapping will take the absolute value of the scale of the object into account.");
publicstaticGUIContentlockWithTilingRateText=newGUIContent("Lock with height map tiling rate","Displacement mapping will take the absolute value of the tiling rate of the height map into account.");
// Per pixel displacement
publicstaticGUIContentenablePerPixelDisplacementText=newGUIContent("Enable Per Pixel Displacement","Per pixel displacement work best with flat surfaces. This is an expensive features and should be enable wisely. Typical use case is paved road.");
// Per pixel displacement
publicstaticGUIContentperPixelDisplacementObjectScaleText=newGUIContent("Lock with object scale","Per Pixel displacement will take into account the tiling scale - Only work with uniform positive scale");
publicstaticGUIContentenableVertexDisplacementText=newGUIContent("Enable vertex displacement","Use heightmap as a displacement map. Displacement map is use to move vertex position in local space");
publicstaticGUIContentvertexDisplacementObjectScaleText=newGUIContent("Lock with object scale","Vertex displacement will take into account the object scale - Only work with uniform positive scale");
publicstaticGUIContentvertexDisplacementTilingScaleText=newGUIContent("Lock with heightmap tiling","Vertex displacement will take into account the tiling scale - Only work with uniform positive scale");
publicstaticGUIContentppdPrimitiveLength=newGUIContent("Primitive length","Dimensions of the primitive (with the scale of 1) to which the per-pixel displacement mapping is being applied. For example, the standard quad is 1 x 1 meter, while the standard plane is 10 x 10 meters.");
publicstaticGUIContentppdPrimitiveWidth=newGUIContent("Primitive width","Dimensions of the primitive (with the scale of 1) to which the per-pixel displacement mapping is being applied. For example, the standard quad is 1 x 1 meter, while the standard plane is 10 x 10 meters.");
publicstaticGUIContentperPixelDisplacementDetailsWarning=newGUIContent("For pixel displacement to work correctly, details and base map must use same UV mapping");
}
// Lit shader is not layered but some layered materials inherit from it. In order to share code we need LitUI to account for this.
// Share by Lit and LayeredLit. Return object scaling for displacement map depends if it is vertex (affect vertex displacement) or pixel displacement (affect tiling)
// 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
ppdParam.uv = layerTexCoord.base.uv; // For planar it is uv too, not uvXZ
float3x3 worldToTangent = input.worldToTangent;
// 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
// 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;
// Apply offset to all UVSet0 / planar
layerTexCoord.base.uv += offset;
layerTexCoord.details.uv += isPlanar ? offset : _UVDetailsMappingMask.x * offset; // Only apply offset if details map use UVSet0 _UVDetailsMappingMask.x will be 1 in this case, else 0
// 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.
// We need to mask out inactive layers so that their height does not impact the result.
float4 maskedHeights = heights * blendMask.argb;
float maxHeight = GetMaxHeight(maskedHeights);
// Make sure that transition is not zero otherwise the next computation will be wrong.
// The epsilon here also has to be bigger than the epsilon in the next computation.
float transition = max(_HeightTransition, 1e-5);
// The goal here is to have all but the highest layer at negative heights, then we add the transition so that if the next highest layer is near transition it will have a positive value.
// Then we clamp this to zero and normalize everything so that highest layer has a value of 1.
maskedHeights = maskedHeights - maxHeight.xxxx;
// We need to add an epsilon here for active layers (hence the blendMask again) so that at least a layer shows up if everything's too low.
// PPD is affecting only one mapping at the same time, mean we need to execute it for each mapping (UV0, UV1, 3 times for triplanar etc..)
// We chose to not support all this case that are extremely hard to manage (for example mixing different mapping, mean it also require different tangent space that is not supported in Unity)
// - Blend Mask use same mapping as main layer (UVO, Planar, Triplanar)
// From these rules it mean that PPD is enable only if the user 1) ask for it, 2) if there is one heightmap enabled on active layer, 3) if mapping is the same for all layer respecting 2), 4) if mapping is UV0, planar or triplanar mapping
// Most contraint are handled by the inspector (i.e the UI) like the mapping constraint and is assumed in the shader.
// Even if we use same mapping we can have different tiling. For per pixel displacement we will perform the ray marching with already tiled uv
float maxHeight = GetMaxDisplacement();
// Compute lod as we will sample inside a loop(so can't use regular sampling)
// Note: It appear that CALCULATE_TEXTURE2D_LOD only return interger lod. We want to use float lod to have smoother transition and fading, so do our own calculation.
// Approximation of lod to used. Be conservative here, we will take the highest mip of all layers.
// Remember, we assume that we used the same mapping for all layer, so only size matter.
// Compute lod as we will sample inside a loop(so can't use regular sampling)
// Note: It appear that CALCULATE_TEXTURE2D_LOD only return interger lod. We want to use float lod to have smoother transition and fading, so do our own calculation.
// Approximation of lod to used. Be conservative here, we will take the highest mip of all layers.
// Remember, we assume that we used the same mapping for all layer, so only size matter.
float2 minUvSize = GetMinUvSize(layerTexCoord);
float lod = ComputeTextureLOD(minUvSize);
float weights[_MAX_LAYER];
ComputeMaskWeights(blendMasks, weights);
// Be sure we are not considering weight here were there is no heightmap
// We need to calculate the texture space direction. It depends on the mapping.
if (isTriplanar)
{
// This is not supported currently
height = 1.0;
NdotV = 1.0;
}
else
{
// For planar it is uv too, not uvXZ
ppdParam.uv[0] = layerTexCoord.base0.uv;
ppdParam.uv[1] = layerTexCoord.base1.uv;
ppdParam.uv[2] = layerTexCoord.base2.uv;
ppdParam.uv[3] = layerTexCoord.base3.uv;
// Apply to all layer that used triplanar
*/
height = 1;
NdotV = 1;
}
else
{
ppdParam.uv[0] = layerTexCoord.base0.uv;
ppdParam.uv[1] = layerTexCoord.base1.uv;
ppdParam.uv[2] = layerTexCoord.base2.uv;
ppdParam.uv[3] = layerTexCoord.base3.uv;
// Note: The TBN is not normalize as it is based on mikkt. We should normalize it, but POM is always use on simple enough surface that mean it is not required (save 2 normalize). Tag: SURFACE_GRADIENT
// Note: worldToTangent is only define for UVSet0, so we expect that layer that use POM have UVSet0
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)
// 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
// For planar the view vector is the world view vector (unless we want to support object triplanar ? and in this case used TransformWorldToObject)
// TODO: do we support object triplanar ? See ComputeLayerTexCoord
// We need to mask out inactive layers so that their height does not impact the result.
float4 maskedHeights = heights * blendMask.argb;
float maxHeight = GetMaxHeight(maskedHeights);
// Make sure that transition is not zero otherwise the next computation will be wrong.
// The epsilon here also has to be bigger than the epsilon in the next computation.
float transition = max(_HeightTransition, 1e-5);
// The goal here is to have all but the highest layer at negative heights, then we add the transition so that if the next highest layer is near transition it will have a positive value.
// Then we clamp this to zero and normalize everything so that highest layer has a value of 1.
maskedHeights = maskedHeights - maxHeight.xxxx;
// We need to add an epsilon here for active layers (hence the blendMask again) so that at least a layer shows up if everything's too low.
float4 opacityAsDensity = saturate((inputAlphaMask - (float4(1.0, 1.0, 1.0, 1.0) - blendMasks.argb)) * 20.0); // 20.0 is the number of steps in inputAlphaMask (Density mask. We decided 20 empirically)
// Note: If per pixel displacement is enabled it mean we will fetch again the various heightmaps at the intersection location. Not sure the compiler can optimize.
// This first set of define allow to say which attributes will be use by the mesh in the vertex and domain shader (for tesselation)
// If we have a layered shader, any UV can be use for this. To reduce the number of variant we groupt UV0/UV1 and UV2/UV3 instead of having variant for UV0/UV1/UV2/UV3
// When UVX is present, we assume that UVX - 1 ... UV0 is present
// This first set of define allow to say which attributes will be use by the mesh in the vertex and domain shader (for tesselation)
// If we have a layered shader, any UV can be use for this. To reduce the number of variant we groupt UV0/UV1 and UV2/UV3 instead of having variant for UV0/UV1/UV2/UV3
// When UVX is present, we assume that UVX - 1 ... UV0 is present
// TODO: This should be an uniform for the object, this code should be remove (and is specific to Lit.shader) once we have it. - Workaround for now
output.objectScale = objectScale;
#endif
// TODO: TEMP: Velocity has a flow as it doens't have normal. This need to be fix. In the mean time, generate fix normal so compiler doesn't complain - When fix, think to also enable ATTRIBUTES_NEED_NORMAL in LitVelocityPass.hlsl