140 lines
5.4 KiB
HLSL
140 lines
5.4 KiB
HLSL
#ifndef GPU_INSTANCER_BILLBOARD_INCLUDED
|
|
#define GPU_INSTANCER_BILLBOARD_INCLUDED
|
|
|
|
void GPUIBillboardVertex(inout float4 vertex, inout float3 normal, inout float4 tangent, inout float3 tangentWorld, inout float3 bitangentWorld,
|
|
inout float3 normalWorld, inout float4 texcoord, inout float2 atlasUV, float frameCount)
|
|
{
|
|
|
|
float3 billboardCameraPos = _WorldSpaceCameraPos;
|
|
|
|
// If shadowcaster, account for billboard:
|
|
//#if defined(UNITY_PASS_SHADOWCASTER)
|
|
// _WorldSpaceLightPos0.w is 0 for directional lights only. Use light vector instead of cam vector if that is the case:
|
|
// billboardCameraPos = _WorldSpaceCameraPos * (1 - _WorldSpaceLightPos0.w) + (_WorldSpaceLightPos0.w * _WorldSpaceLightPos0.xyz);
|
|
//#endif
|
|
|
|
float3 billboardCameraDir = normalize(mul((float3x3) unity_WorldToObject, billboardCameraPos - unity_ObjectToWorld._m03_m13_m23));
|
|
|
|
// get current camera angle in radians
|
|
float angle = atan2(-billboardCameraDir.z, billboardCameraDir.x);
|
|
|
|
// calculate current frame index and set uvs
|
|
float frameIndex = round((angle - UNITY_PI / 2) / (UNITY_TWO_PI / frameCount));
|
|
atlasUV = texcoord.xy * float2(1 / frameCount, 1) + float2((1 / frameCount) * frameIndex, 0);
|
|
|
|
// calculate camera vectors
|
|
float3 up = float3(0, 1, 0);
|
|
float3 forward = -normalize(UNITY_MATRIX_V._m20_m21_m22);
|
|
|
|
#ifdef _BILLBOARDFACECAMPOS_ON
|
|
float3 right = normalize(cross(float3(0, 1, 0), unity_ObjectToWorld._m03_m13_m23 - billboardCameraPos));
|
|
#else
|
|
float3 right = normalize(UNITY_MATRIX_V._m00_m01_m02);
|
|
// adjust rotation matrix if camera is upside down
|
|
right *= sign(normalize(UNITY_MATRIX_V._m10_m11_m12).y);
|
|
#endif
|
|
|
|
// create camera rotation matrix
|
|
float4x4 rotationMatrix = float4x4(right, 0, up, 0, forward, 0, 0, 0, 0, 1);
|
|
|
|
// rotate to camera lookAt
|
|
vertex.x *= length(unity_ObjectToWorld._m00_m10_m20);
|
|
vertex.y *= length(unity_ObjectToWorld._m01_m11_m21);
|
|
vertex.z *= length(unity_ObjectToWorld._m02_m12_m22);
|
|
vertex = mul(vertex, rotationMatrix);
|
|
|
|
// account for world position
|
|
vertex.xyz += unity_ObjectToWorld._m03_m13_m23;
|
|
|
|
// ignore initial object rotation for the billboard
|
|
vertex = mul(unity_WorldToObject, vertex);
|
|
vertex.y += (1 / frameCount * unity_ObjectToWorld[3].y + unity_ObjectToWorld[3].z) * length(unity_ObjectToWorld._m01_m11_m21);
|
|
|
|
// adjust normal and tangents
|
|
normal = -billboardCameraDir;
|
|
tangent.xyz = normalize(cross(up, normal));
|
|
tangent.w = -1;
|
|
|
|
// interpolate normal and tangents to surface func
|
|
normalWorld = normal;
|
|
tangentWorld = tangent.xyz;
|
|
bitangentWorld = cross(normalWorld, tangentWorld) * (tangent.w * unity_WorldTransformParams.w);
|
|
}
|
|
|
|
half4 GPUIBillboardNormals(sampler2D _NormalAtlas, float2 uvAtlas, float frameCount, float3 tangentWorld, float3 bitangentWorld, float3 normalWorld)
|
|
{
|
|
// read Normal Atlas
|
|
half4 normalTexture = tex2D(_NormalAtlas, uvAtlas);
|
|
|
|
// remap normalTexture back to [-1, 1]
|
|
half3 unpackedNormalTexture = (normalTexture.xyz * 2.0 - 1.0);
|
|
|
|
// modify normal map with the billboard world vectors
|
|
float3 normal = normalize(mul(half3x3(tangentWorld, bitangentWorld, normalWorld), unpackedNormalTexture));
|
|
|
|
// calculate depth
|
|
half depth = max(normalTexture.w - 0.35, 0);
|
|
|
|
return half4(normal, depth);
|
|
}
|
|
|
|
void CalculateHueVariation(half4 hueColor, inout half4 originalColor)
|
|
{
|
|
float3 worldPosActual = unity_ObjectToWorld._m03_m13_m23;
|
|
float hueVariationAmount = frac(worldPosActual.x + worldPosActual.y + worldPosActual.z);
|
|
hueVariationAmount = saturate(hueVariationAmount * hueColor.a);
|
|
half3 shiftedColor = lerp(originalColor.rgb, hueColor.rgb, hueVariationAmount);
|
|
|
|
half maxBase = max(originalColor.r, max(originalColor.g, originalColor.b));
|
|
half newMaxBase = max(shiftedColor.r, max(shiftedColor.g, shiftedColor.b));
|
|
maxBase /= newMaxBase;
|
|
maxBase = maxBase * 0.5f + 0.5f;
|
|
|
|
shiftedColor.rgb *= maxBase;
|
|
originalColor.rgb = saturate(shiftedColor);
|
|
}
|
|
|
|
// Does not calculate worldPos in the surface function
|
|
void CalculateHueVariationVulkan(half4 hueColor, inout half4 originalColor, float3 worldPos)
|
|
{
|
|
float hueVariationAmount = frac(worldPos.x + worldPos.y + worldPos.z);
|
|
|
|
// Vulkan surface shader worldpos fix
|
|
if (hueVariationAmount > 0.999999){
|
|
hueVariationAmount = 0;
|
|
}
|
|
|
|
hueVariationAmount = saturate(hueVariationAmount * hueColor.a);
|
|
half3 shiftedColor = lerp(originalColor.rgb, hueColor.rgb, hueVariationAmount);
|
|
|
|
half maxBase = max(originalColor.r, max(originalColor.g, originalColor.b));
|
|
half newMaxBase = max(shiftedColor.r, max(shiftedColor.g, shiftedColor.b));
|
|
maxBase /= newMaxBase;
|
|
maxBase = maxBase * 0.5f + 0.5f;
|
|
|
|
shiftedColor.rgb *= maxBase;
|
|
originalColor.rgb = saturate(shiftedColor);
|
|
}
|
|
|
|
inline float LinearToGammaExact(float value)
|
|
{
|
|
if (value <= 0.0F)
|
|
return 0.0F;
|
|
else if (value <= 0.0031308F)
|
|
return 12.92F * value;
|
|
else if (value < 1.0F)
|
|
return 1.055F * pow(value, 0.4166667F) - 0.055F;
|
|
else
|
|
return pow(value, 0.45454545F);
|
|
}
|
|
|
|
// uses the exact (and more expansive) version of LinearToGammaSpace. Used only for baking.
|
|
inline half3 LinearToGamma(half3 linRGB)
|
|
{
|
|
linRGB = max(linRGB, half3(0.h, 0.h, 0.h));
|
|
|
|
// Exact version of the LineatToGammeSpace from UnityCG.cginc:
|
|
return half3(LinearToGammaExact(linRGB.r), LinearToGammaExact(linRGB.g), LinearToGammaExact(linRGB.b));
|
|
}
|
|
|
|
#endif |