Files
2022-01-22 20:13:49 -08:00

349 lines
10 KiB
HLSL
Vendored

// Alloy Physical Shader Framework
// Copyright 2013-2017 RUST LLC.
// http://www.alloy.rustltd.com/
/////////////////////////////////////////////////////////////////////////////////
/// @file Utility.cginc
/// @brief Minimum functions and constants common to surfaces and particles.
/////////////////////////////////////////////////////////////////////////////////
#ifndef ALLOY_SHADERS_FRAMEWORK_UTILITY_CGINC
#define ALLOY_SHADERS_FRAMEWORK_UTILITY_CGINC
#include "Assets/Alloy/Shaders/Config.cginc"
#include "UnityShaderVariables.cginc"
/// Defines all texture transform uniform variables, inlcuding additional transforms.
/// Spin is in radians.
#define A_SAMPLER_2D(name) \
sampler2D name; \
float4 name##_ST; \
float2 name##Velocity; \
float name##Spin; \
float name##UV;
/// Allows passing all of a texture's transform uniforms into a function.
#define A_SAMPLER_2D_INPUT(name) name, name##_ST, name##Velocity, name##UV
/// Allows accessing all of a texture's transform uniforms inside a function.
#define A_SAMPLER_PARAM(name) sampler2D name, float4 name##_ST, float2 name##Velocity, float name##UV
// NOTE: To make it rotate around a "center" point, the order of operations
// needs to be offset, rotate, scale. So that means that we have to apply
// offset & scroll first divided by tiling. Then when we apply tiling later
// it will cancel.
/// Applies our scrolling effect.
#ifdef A_TEX_SCROLL_OFF
#define A_TEX_SCROLL(name, tex) (tex)
#else
#define A_TEX_SCROLL(name, tex) (tex + ((name##Velocity * _Time.y + name##_ST.zw) / name##_ST.xy))
#endif
/// Applies our spinning effect.
#define A_TEX_SPIN(name, tex) (aRotateTextureCoordinates(name##Spin * _Time.y, tex.xy))
/// Applies Unity texture transforms plus our spinning effect.
#define A_TEX_TRANSFORM_SPIN(name, tex) (A_TEX_SPIN(name, tex + (name##_ST.zw / name##_ST.xy)) * name##_ST.xy)
/// Applies Unity texture transforms plus our spinning and scrolling effects.
#define A_TEX_TRANSFORM_SCROLL(name, tex) (A_TEX_SCROLL(name, tex) * name##_ST.xy)
/// Applies Unity texture transforms plus our spinning and scrolling effects.
#define A_TEX_TRANSFORM_SCROLL_SPIN(name, tex) (A_TEX_SPIN(name, A_TEX_SCROLL(name, tex)) * name##_ST.xy)
/// A value close to zero.
/// This is used for preventing NaNs in cases where you can divide by zero.
static const float A_EPSILON = 1e-6f;
/// Multi-component zero.
static const half3 A_ZERO = half3(0.0h, 0.0h, 0.0h);
/// Multi-component zero.
static const half4 A_ZERO4 = half4(0.0h, 0.0h, 0.0h, 0.0h);
/// Multi-component one.
static const half3 A_ONE = half3(1.0h, 1.0h, 1.0h);
/// Multi-component one.
static const half4 A_ONE4 = half4(1.0h, 1.0h, 1.0h, 1.0h);
/// Color Black.
static const half3 A_BLACK = A_ZERO;
/// Multi-component zero.
static const half4 A_BLACK4 = A_ZERO4;
/// Color White.
static const half3 A_WHITE = A_ONE;
/// Multi-component one.
static const half4 A_WHITE4 = A_ONE4;
/// X-Axis normal.
static const half3 A_AXIS_X = half3(1.0h, 0.0h, 0.0h);
/// Y-Axis normal.
static const half3 A_AXIS_Y = half3(0.0h, 1.0h, 0.0h);
/// Z-Axis normal.
static const half3 A_AXIS_Z = half3(0.0h, 0.0h, 1.0h);
/// Flat normal in tangent space.
static const half3 A_FLAT_NORMAL = A_AXIS_Z;
/// Applies 2D texture rotation around the point (0.5,0.5) in UV-space.
/// @param rotation Rotation in radians.
/// @param texcoords Texture coordinates to be rotated.
/// @return Rotated texture coordinates.
float2 aRotateTextureCoordinates(
float rotation,
float2 texcoords)
{
// Texture Rotation
// cf http://forum.unity3d.com/threads/rotation-of-texture-uvs-directly-from-a-shader.150482/#post-1031763
float2 centerOffset = float2(0.5f, 0.5f);
float sinTheta = sin(rotation);
float cosTheta = cos(rotation);
float2x2 rotationMatrix = float2x2(cosTheta, -sinTheta, sinTheta, cosTheta);
return mul(texcoords - centerOffset, rotationMatrix) + centerOffset;
}
/// Dot product of two vectors, clamped to range [0,1].
half aDotClamp(
half2 x,
half2 y)
{
return saturate(dot(x, y));
}
/// Dot product of two vectors, clamped to range [0,1].
half aDotClamp(
half3 x,
half3 y)
{
return saturate(dot(x, y));
}
/// Dot product of two vectors, clamped to range [0,1].
half aDotClamp(
half4 x,
half4 y)
{
return saturate(dot(x, y));
}
/// Screen Blends two colors.
half3 aScreenBlend(
half3 a,
half3 b)
{
return A_ONE - ((A_ONE - a) * (A_ONE - b));
}
/// Converts an LDR color from gamma-space to linear-space.
half3 aGammaToLinear(
half3 sRGB)
{
// sRGB curve approximation.
// cf http://chilliant.blogspot.com.au/2012/08/srgb-approximations-for-hlsl.html?m=1
return sRGB * (sRGB * (sRGB * 0.305306011h + 0.682171111h) + 0.012522878h);
}
/// Converts an LDR value from gamma-space to linear-space.
half aGammaToLinear(
half sRGB)
{
return aGammaToLinear(sRGB.rrr).r;
}
/// Interpolate from one to another value.
half aLerpOneTo(
half b,
half alpha)
{
// Use lerp intrinsic for better optimization.
return lerp(1.0h, b, alpha);
}
/// Interpolate from the color white to another color.
half3 aLerpWhiteTo(
half3 b,
half alpha)
{
// Use lerp intrinsic for better optimization.
return lerp(A_WHITE, b, alpha);
}
/// Calculates a linear color's luminance.
/// @param color Linear LDR color.
/// @return Color's chromaticity.
half aLuminance(
half3 color)
{
// Linear-space luminance coefficients.
// cf https://en.wikipedia.org/wiki/Luma_(video)
return dot(color, half3(0.2126h, 0.7152h, 0.0722h));
}
/// Calculates a linear color's chromaticity.
/// @param color Linear LDR color.
/// @return Color's chromaticity.
half3 aChromaticity(
half3 color)
{
return color / max(aLuminance(color), A_EPSILON).rrr;
}
/// Clamp HDR output to avoid excess bloom and blending errors.
/// @param value Linear HDR value.
/// @return Range-limited HDR color [0,32].
half aHdrClamp(
half value)
{
#if A_USE_HDR_CLAMP
value = min(value, A_HDR_CLAMP_MAX_INTENSITY);
#endif
return value;
}
/// Clamp HDR output to avoid excess bloom and blending errors.
/// @param color Linear HDR color.
/// @return Range-limited HDR color [0,32].
half3 aHdrClamp(
half3 color)
{
#if A_USE_HDR_CLAMP
color = min(color, (A_HDR_CLAMP_MAX_INTENSITY).rrr);
#endif
return color;
}
/// Clamp HDR output to avoid excess bloom and blending errors.
/// @param color Linear HDR color.
/// @return Range-limited HDR color [0,32].
half4 aHdrClamp(
half4 color)
{
#if A_USE_HDR_CLAMP
color = min(color, (A_HDR_CLAMP_MAX_INTENSITY).rrrr);
#endif
return color;
}
/// Used to calculate a rim light effect.
/// @param weight Scales the intensity of the effect.
/// @param bias Bias rim towards constant emission.
/// @param power Rim falloff.
/// @param NdotV Normal and view vector dot product.
/// @return Rim lighting.
half aRimLight(
half weight,
half bias,
half power,
half NdotV)
{
return weight * lerp(bias, 1.0h, pow(1.0h - NdotV, power));
}
/// Gets distance from a point to an Axis-Aligned Bounding Box.
/// @param p Starting point.
/// @param aabbMin AABB min extents.
/// @param aabbMax AABB max extents.
/// @return Per-axis distance from AABB extents.
half3 aDistanceFromAabb(
half3 p,
half3 aabbMin,
half3 aabbMax)
{
return max(max(p - aabbMax, aabbMin - p), half3(0.0h, 0.0h, 0.0h));
}
/// Applies four closest lights per-vertex using Alloy's attenuation.
/// @param lightPosX Four lights' position X in world-space.
/// @param lightPosY Four lights' position Y in world-space.
/// @param lightPosZ Four lights' position Z in world-space.
/// @param lightColor0 First light color.
/// @param lightColor1 Second light color.
/// @param lightColor2 Third light color.
/// @param lightColor3 Fourth light color.
/// @param lightAttenSq Four lights' Unity attenuation.
/// @param positionWorld Position in world-space.
/// @param normalWorld Normal in world-space.
/// @return Per-vertex direct lighting.
float3 aShade4PointLights(
float4 lightPosX,
float4 lightPosY,
float4 lightPosZ,
float3 lightColor0,
float3 lightColor1,
float3 lightColor2,
float3 lightColor3,
float4 lightAttenSq,
float3 positionWorld,
float3 normalWorld)
{
// to light vectors
float4 toLightX = lightPosX - positionWorld.x;
float4 toLightY = lightPosY - positionWorld.y;
float4 toLightZ = lightPosZ - positionWorld.z;
// squared lengths
float4 lengthSq = 0;
lengthSq += toLightX * toLightX;
lengthSq += toLightY * toLightY;
lengthSq += toLightZ * toLightZ;
// NdotL
float4 ndotl = 0;
ndotl += toLightX * normalWorld.x;
ndotl += toLightY * normalWorld.y;
ndotl += toLightZ * normalWorld.z;
// correct NdotL
float4 corr = rsqrt(lengthSq);
ndotl = max (float4(0.0f, 0.0f, 0.0f, 0.0f), ndotl * corr);
// attenuation
#if A_USE_UNITY_ATTENUATION
float4 atten = 1.0 / (1.0 + lengthSq * lightAttenSq);
#else
// NOTE: Get something close to Alloy attenuation by undoing Unity's calculations.
// http://forum.unity3d.com/threads/easiest-way-to-change-point-light-attenuation-with-deferred-path.254337/#post-1681835
float4 invRangeSqr = lightAttenSq / 25.0f;
// Inverse Square attenuation, with light range falloff.
// cf http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p12
float4 ratio2 = lengthSq * invRangeSqr;
float4 num = saturate(float4(1.0f, 1.0f, 1.0f, 1.0f) - (ratio2 * ratio2));
float4 atten = (num * num) / (lengthSq + float4(1.0f, 1.0f, 1.0f, 1.0f));
#endif
float4 diff = ndotl * atten;
// final color
float3 col = 0;
col += lightColor0 * diff.x;
col += lightColor1 * diff.y;
col += lightColor2 * diff.z;
col += lightColor3 * diff.w;
return col;
}
/// Applies four closest lights per-vertex using Alloy's attenuation.
/// @param positionWorld Position in world-space.
/// @param normalWorld Normal in world-space.
/// @return Per-vertex direct lighting.
float3 aShade4PointLights(
float3 positionWorld,
float3 normalWorld)
{
return aShade4PointLights(
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
unity_4LightAtten0, positionWorld, normalWorld);
}
#endif // ALLOY_SHADERS_FRAMEWORK_UTILITY_CGINC