100 lines
4.1 KiB
HLSL
100 lines
4.1 KiB
HLSL
|
|
float4 triplanar(float3 vertPos, float3 normal, float scale, sampler2D tex) {
|
||
|
|
|
||
|
|
// Calculate triplanar coordinates
|
||
|
|
float2 uvX = vertPos.zy * scale;
|
||
|
|
float2 uvY = vertPos.xz * scale;
|
||
|
|
float2 uvZ = vertPos.xy * scale;
|
||
|
|
|
||
|
|
float4 colX = tex2D (tex, uvX);
|
||
|
|
float4 colY = tex2D (tex, uvY);
|
||
|
|
float4 colZ = tex2D (tex, uvZ);
|
||
|
|
// Square normal to make all values positive + increase blend sharpness
|
||
|
|
float3 blendWeight = normal * normal;
|
||
|
|
// Divide blend weight by the sum of its components. This will make x + y + z = 1
|
||
|
|
blendWeight /= dot(blendWeight, 1);
|
||
|
|
return colX * blendWeight.x + colY * blendWeight.y + colZ * blendWeight.z;
|
||
|
|
}
|
||
|
|
|
||
|
|
float4 triplanarOffset(float3 vertPos, float3 normal, float3 scale, sampler2D tex, float2 offset) {
|
||
|
|
float3 scaledPos = vertPos / scale;
|
||
|
|
float4 colX = tex2D (tex, scaledPos.zy + offset);
|
||
|
|
float4 colY = tex2D(tex, scaledPos.xz + offset);
|
||
|
|
float4 colZ = tex2D (tex,scaledPos.xy + offset);
|
||
|
|
|
||
|
|
// Square normal to make all values positive + increase blend sharpness
|
||
|
|
float3 blendWeight = normal * normal;
|
||
|
|
// Divide blend weight by the sum of its components. This will make x + y + z = 1
|
||
|
|
blendWeight /= dot(blendWeight, 1);
|
||
|
|
return colX * blendWeight.x + colY * blendWeight.y + colZ * blendWeight.z;
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 ObjectToTangentVector(float4 tangent, float3 normal, float3 objectSpaceVector) {
|
||
|
|
float3 normalizedTangent = normalize(tangent.xyz);
|
||
|
|
float3 binormal = cross(normal, normalizedTangent) * tangent.w;
|
||
|
|
float3x3 rot = float3x3 (normalizedTangent, binormal, normal);
|
||
|
|
return mul(rot, objectSpaceVector);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Reoriented Normal Mapping
|
||
|
|
// http://blog.selfshadow.com/publications/blending-in-detail/
|
||
|
|
// Altered to take normals (-1 to 1 ranges) rather than unsigned normal maps (0 to 1 ranges)
|
||
|
|
float3 blend_rnm(float3 n1, float3 n2)
|
||
|
|
{
|
||
|
|
n1.z += 1;
|
||
|
|
n2.xy = -n2.xy;
|
||
|
|
|
||
|
|
return n1 * dot(n1, n2) / n1.z - n2;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Sample normal map with triplanar coordinates
|
||
|
|
// Returned normal will be in obj/world space (depending whether pos/normal are given in obj or world space)
|
||
|
|
// Based on: medium.com/@bgolus/normal-mapping-for-a-triplanar-shader-10bf39dca05a
|
||
|
|
float3 triplanarNormal(float3 vertPos, float3 normal, float3 scale, float2 offset, sampler2D normalMap) {
|
||
|
|
float3 absNormal = abs(normal);
|
||
|
|
|
||
|
|
// Calculate triplanar blend
|
||
|
|
float3 blendWeight = saturate(pow(normal, 4));
|
||
|
|
// Divide blend weight by the sum of its components. This will make x + y + z = 1
|
||
|
|
blendWeight /= dot(blendWeight, 1);
|
||
|
|
|
||
|
|
// Calculate triplanar coordinates
|
||
|
|
float2 uvX = vertPos.zy * scale + offset;
|
||
|
|
float2 uvY = vertPos.xz * scale + offset;
|
||
|
|
float2 uvZ = vertPos.xy * scale + offset;
|
||
|
|
|
||
|
|
// Sample tangent space normal maps
|
||
|
|
// UnpackNormal puts values in range [-1, 1] (and accounts for DXT5nm compression)
|
||
|
|
float3 tangentNormalX = UnpackNormal(tex2D(normalMap, uvX));
|
||
|
|
float3 tangentNormalY = UnpackNormal(tex2D(normalMap, uvY));
|
||
|
|
float3 tangentNormalZ = UnpackNormal(tex2D(normalMap, uvZ));
|
||
|
|
|
||
|
|
// Swizzle normals to match tangent space and apply reoriented normal mapping blend
|
||
|
|
tangentNormalX = blend_rnm(half3(normal.zy, absNormal.x), tangentNormalX);
|
||
|
|
tangentNormalY = blend_rnm(half3(normal.xz, absNormal.y), tangentNormalY);
|
||
|
|
tangentNormalZ = blend_rnm(half3(normal.xy, absNormal.z), tangentNormalZ);
|
||
|
|
|
||
|
|
// Apply input normal sign to tangent space Z
|
||
|
|
float3 axisSign = sign(normal);
|
||
|
|
tangentNormalX.z *= axisSign.x;
|
||
|
|
tangentNormalY.z *= axisSign.y;
|
||
|
|
tangentNormalZ.z *= axisSign.z;
|
||
|
|
|
||
|
|
// Swizzle tangent normals to match input normal and blend together
|
||
|
|
float3 outputNormal = normalize(
|
||
|
|
tangentNormalX.zyx * blendWeight.x +
|
||
|
|
tangentNormalY.xzy * blendWeight.y +
|
||
|
|
tangentNormalZ.xyz * blendWeight.z
|
||
|
|
);
|
||
|
|
|
||
|
|
return outputNormal;
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 triplanarNormalTangentSpace(float3 vertPos, float3 normal, float3 scale, float4 tangent, sampler2D normalMap) {
|
||
|
|
float3 textureNormal = triplanarNormal(vertPos, normal, scale, 0, normalMap);
|
||
|
|
return ObjectToTangentVector(tangent, normal, textureNormal);
|
||
|
|
}
|
||
|
|
|
||
|
|
float3 triplanarNormalTangentSpace(float3 vertPos, float3 normal, float3 scale, float2 offset, float4 tangent, sampler2D normalMap) {
|
||
|
|
float3 textureNormal = triplanarNormal(vertPos, normal, scale, offset, normalMap);
|
||
|
|
return ObjectToTangentVector(tangent, normal, textureNormal);
|
||
|
|
}
|