#ifndef FOG_VOLUME_COMMON_INCLUDED
|
|
#define FOG_VOLUME_COMMON_INCLUDED
|
|
float3 VolumeSpaceCoords = 0;
|
|
half VerticalGrad;
|
|
half2 heightGradient;
|
|
half HeightAtten;
|
|
float3 VolumeSpaceCoordsWorldSpace;
|
|
|
|
//#ifdef VOLUMETRIC_SHADOWS
|
|
half3 VolumeShadow = 1;
|
|
#define _GeneralShadowOffset 0.001f
|
|
#define bias 0
|
|
//Shadow params
|
|
uniform float4 _ShadowCameraPosition;
|
|
uniform float4x4 _ShadowCameraProjection;
|
|
uniform float _ShadowCameraSize;
|
|
uniform sampler2D _ShadowTexture;
|
|
//#define _VolumetricShadowsEnabled 0
|
|
uniform int _VolumetricShadowsEnabled = 0;
|
|
float4 when_eq(float4 x, float4 y) {
|
|
return 1.0 - abs(sign(x - y));
|
|
}
|
|
float3 when_eq(float3 x, float3 y) {
|
|
return 1.0 - abs(sign(x - y));
|
|
}
|
|
float when_eq(float x, float y) {
|
|
return 1.0 - abs(sign(x - y));
|
|
}
|
|
float FrameShadow(in float2 shadowUVs, in float3 worldPos) {
|
|
|
|
float FinalShadow = 1;
|
|
float2 ShadowMap = tex2Dlod(_ShadowTexture, float4(shadowUVs, 0, 0)).rg;
|
|
float d = ShadowMap.x;
|
|
|
|
//if (d < .0001)FinalShadow = 1.0;
|
|
//else
|
|
//{
|
|
float theLength = distance(worldPos, _ShadowCameraPosition);
|
|
FinalShadow = theLength < d + bias ? 1.0f : 0.0f;
|
|
|
|
//}
|
|
|
|
//fade before reaching the end
|
|
half edges = ShadowMap.y;
|
|
return lerp(1,FinalShadow, edges);
|
|
}
|
|
//#endif
|
|
|
|
float sdSphere(float3 p, float s)
|
|
{
|
|
return (length(p) - s);
|
|
}
|
|
|
|
float sdBox(float3 p, float3 b)
|
|
{
|
|
float3 d = abs(p) - b;
|
|
d = (min(max(d.x, max(d.y, d.z)), 0.0) + length(max(d, 0.0)));
|
|
|
|
return d;
|
|
}
|
|
|
|
float udBox(float3 p, float3 b)
|
|
{
|
|
return length(max(abs(p) - b, 0.0));
|
|
}
|
|
|
|
float PrimitiveShape(fixed ShapeType, float3 p, float3 s)
|
|
{
|
|
if (ShapeType <= 1.0f)//Box
|
|
return sdBox(p, s);
|
|
else
|
|
|
|
if (ShapeType <= 2.0f)//Sphere
|
|
return sdSphere(p, s);
|
|
|
|
else return .0f;
|
|
}
|
|
|
|
|
|
float nrand(float2 ScreenUVs)
|
|
{
|
|
return frac(sin(ScreenUVs.x * 12.9898 + ScreenUVs.y * 78.233) * 43758.5453);
|
|
}
|
|
|
|
float remap_tri(float v)
|
|
{
|
|
float orig = v * 2.0 - 1.0;
|
|
v = max(-1.0, orig / sqrt(abs(orig)));
|
|
|
|
return v - sign(orig) + 0.5;
|
|
}
|
|
|
|
bool IntersectBox(float3 startpoint, float3 direction, float3 boxmin, float3 boxmax, out float tnear, out float tfar)
|
|
{
|
|
// compute intersection of ray with all six bbox planes
|
|
float3 invR = 1.0 / direction;
|
|
float3 tbot = invR * (boxmin.xyz - startpoint);
|
|
float3 ttop = invR * (boxmax.xyz - startpoint);
|
|
// re-order intersections to find smallest and largest on each axis
|
|
float3 tmin = min(ttop, tbot);
|
|
float3 tmax = max(ttop, tbot);
|
|
// find the largest tmin and the smallest tmax
|
|
float2 t0 = max(tmin.xx, tmin.yz);
|
|
tnear = max(t0.x, t0.y);
|
|
t0 = min(tmax.xx, tmax.yz);
|
|
tfar = min(t0.x, t0.y);
|
|
// check for hit
|
|
bool hit;
|
|
if ((tnear > tfar))
|
|
hit = false;
|
|
else
|
|
hit = true;
|
|
return hit;
|
|
}
|
|
half3 ToneMap(half3 x, half exposure)
|
|
{
|
|
//Photographic
|
|
return 1 - exp2(-x * exposure);
|
|
}
|
|
#define PI 3.1416
|
|
//http://zurich.disneyresearch.com/~wjarosz/publications/dissertation/chapter4.pdf
|
|
float Henyey(float3 E, float3 L, float mieDirectionalG)
|
|
{
|
|
float theta = saturate(dot(E, L));
|
|
return (1.0 / (4.0 * PI)) * ((1.0 - mieDirectionalG * mieDirectionalG) / pow(1.0 - 2.0 * mieDirectionalG * theta + mieDirectionalG * mieDirectionalG, 1.5));
|
|
}
|
|
|
|
half3 ContrastF(half3 pixelColor, fixed contrast)
|
|
{
|
|
return saturate(((pixelColor.rgb - 0.5f) * max(contrast, 0)) + 0.5f);
|
|
}
|
|
|
|
float3 rotate(float3 p, float rot)
|
|
{
|
|
float3 r = 0;
|
|
#ifdef Twirl_X
|
|
float3x3 rx = float3x3(1.0, 0.0, 0.0, 0.0, cos(rot), sin(rot), 0.0, -sin(rot), cos(rot));
|
|
r = mul(p, rx);
|
|
#endif
|
|
|
|
#ifdef Twirl_Y
|
|
float3x3 ry = float3x3(cos(rot), 0.0, -sin(rot), 0.0, 1.0, 0.0, sin(rot), 0.0, cos(rot));
|
|
r = mul(p, ry);
|
|
#endif
|
|
|
|
#ifdef Twirl_Z
|
|
float3x3 rz = float3x3(cos(rot), -sin(rot), 0.0, sin(rot), cos(rot), 0.0, 0.0, 0.0, 1.0);
|
|
r = mul(p, rz);
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
half HeightGradient(float H, half H0, half Hmax)
|
|
{
|
|
return saturate((H - H0) / (Hmax - H0));
|
|
}
|
|
|
|
half Threshold(float a, float Gain, float Contrast)
|
|
{
|
|
float input = a * Gain;
|
|
//float thresh = input - Contrast;
|
|
float thresh = ContrastF(input, Contrast);
|
|
return saturate(lerp(0.0f, input, thresh));
|
|
}
|
|
half PrimitiveAccum = 1;
|
|
#ifdef _FOG_VOLUME_NOISE
|
|
|
|
float NoiseSamplerLoop(float3 p)
|
|
{
|
|
float n = 0, iter = 1;
|
|
|
|
for (int i = 0; i < Octaves; i++)
|
|
{
|
|
p /= 1 + i*.06;
|
|
p += Speed.rgb *_BaseRelativeSpeed * (i*.15 + 1);
|
|
|
|
#ifdef EARTH_CLOUD_STYLE
|
|
n += tex3Dlod(_NoiseVolume, float4(p * float3(.5, 1, .5), 0));
|
|
n += tex3Dlod(_NoiseVolume, float4(p*.3, 0))*2+ (heightGradient.x);
|
|
|
|
n = (n-1); //n *= n*.57;
|
|
|
|
#else
|
|
|
|
n += tex3Dlod(_NoiseVolume, float4(p, 0));
|
|
#endif
|
|
}
|
|
|
|
n = n / Octaves;
|
|
|
|
/*if (Octaves > 1)
|
|
{
|
|
n++;
|
|
n *= tex3Dlod(_NoiseVolume, float4(p *.75, 0));
|
|
}*/
|
|
return n;
|
|
}
|
|
|
|
float noise(in float3 p, half DistanceFade)
|
|
{
|
|
float Volume = 0;
|
|
float lowFreqScale = BaseTiling;
|
|
|
|
|
|
half NoiseBaseLayers = 0;
|
|
|
|
if (Coverage > 0.01)
|
|
NoiseBaseLayers = NoiseSamplerLoop(p * lowFreqScale);
|
|
|
|
#ifdef DF
|
|
NoiseBaseLayers = Threshold(NoiseBaseLayers, Coverage * NoiseAtten*PrimitiveAccum, threshold);
|
|
#else
|
|
NoiseBaseLayers = Threshold(NoiseBaseLayers, Coverage * NoiseAtten, threshold);
|
|
#endif
|
|
half NoiseDetail = 0;
|
|
half BaseMask = saturate((1 - NoiseBaseLayers * _DetailMaskingThreshold));
|
|
|
|
if (DistanceFade > 0 && NoiseBaseLayers>0 && BaseMask>0)//no me samplees donde ya es opaco del tó
|
|
{
|
|
NoiseDetail += BaseMask*DistanceFade*(tex3Dlod(_NoiseVolume, float4(p*DetailTiling + Speed * _DetailRelativeSpeed, 0)).r);
|
|
|
|
if (Octaves > 1 )
|
|
NoiseDetail += DistanceFade*(tex3Dlod(_NoiseVolume,
|
|
//size and offset
|
|
float4(p * .5 * DetailTiling + .5
|
|
//distortion
|
|
+ NoiseDetail * _Curl * BaseMask * 2.0 - 1.0
|
|
//animation
|
|
+ Speed * _DetailRelativeSpeed, 0)).r) * 1.5 * BaseMask;
|
|
|
|
NoiseDetail = Threshold(NoiseDetail, 1, 0);
|
|
}
|
|
|
|
//base layer (coverage)
|
|
Volume += NoiseBaseLayers;
|
|
|
|
//add detail layer
|
|
|
|
Volume -= NoiseDetail * _NoiseDetailRange;
|
|
Volume *= 1 + _NoiseDetailRange;
|
|
Volume *= NoiseDensity;
|
|
|
|
return saturate(Volume);
|
|
}
|
|
|
|
//http://flafla2.github.io/2016/10/01/raymarching.html
|
|
float3 calcNormal(in float3 pos, half DistanceFade)
|
|
{
|
|
// epsilon - used to approximate dx when taking the derivative
|
|
const float2 eps = float2(NormalDistance, 0.0);
|
|
|
|
// The idea here is to find the "gradient" of the distance field at pos
|
|
// Remember, the distance field is not boolean - even if you are inside an object
|
|
// the number is negative, so this calculation still works.
|
|
// Essentially you are approximating the derivative of the distance field at this point.
|
|
float3 nor = float3(
|
|
noise(pos + eps.xyy, DistanceFade).x - noise(pos - eps.xyy, DistanceFade).x,
|
|
noise(pos + eps.yxy, DistanceFade).x - noise(pos - eps.yxy, DistanceFade).x,
|
|
noise(pos + eps.yyx, DistanceFade).x - noise(pos - eps.yyx, DistanceFade).x);
|
|
return normalize(nor);
|
|
}
|
|
#endif
|
|
|
|
|
|
#if _FOG_VOLUME_NOISE && _SHADE
|
|
|
|
half ShadowGrad(half ShadowThreshold, half SampleDiff)
|
|
{
|
|
return saturate(ShadowThreshold / (ShadowThreshold - SampleDiff));
|
|
}
|
|
half ShadowShift = .05;
|
|
//#define _SelfShadowSteps 20
|
|
float Shadow(float3 ShadowCoords, v2f i, half detailDist, half3 LightVector, half NoiseAtten)
|
|
{
|
|
|
|
float ShadowThreshold = noise(ShadowCoords, detailDist);
|
|
float3 LightStep = /*_LightLocalDirection*/LightVector / (float)_SelfShadowSteps;
|
|
|
|
float accum = 0;
|
|
float3 ShadowCoordsStep = ShadowCoords;
|
|
for (int k = 0; k <= _SelfShadowSteps && NoiseAtten>0; k++, ShadowCoordsStep += LightStep)
|
|
{
|
|
float NoiseSample = 0;
|
|
float SampleDiff = 0;
|
|
if (ShadowThreshold > 0 && accum < 1)
|
|
{
|
|
NoiseSample = noise(ShadowCoordsStep, detailDist);
|
|
SampleDiff = ShadowThreshold - NoiseSample;
|
|
|
|
|
|
if (SampleDiff <= 0)
|
|
{
|
|
if (ShadowThreshold > 0)
|
|
{
|
|
accum += 1 - ShadowGrad(ShadowThreshold, SampleDiff);
|
|
}
|
|
else
|
|
{
|
|
accum += 1;
|
|
}
|
|
}
|
|
else
|
|
if (ShadowThreshold <= 0)
|
|
{
|
|
accum += ShadowGrad(ShadowThreshold, SampleDiff);
|
|
}
|
|
}
|
|
}
|
|
//return accum;
|
|
return saturate(1 - accum);
|
|
|
|
}
|
|
#endif
|
|
|
|
|
|
float4 PointLight(float3 LightPosition, float3 VolumePosition,
|
|
half size, half3 color, half LightAttenuation, v2f i)
|
|
{
|
|
//#define ATTEN_METHOD_2
|
|
half d = distance(LightPosition, VolumePosition) / size;
|
|
float Attenuation = 0;
|
|
half UnityScaleMatch = 5;
|
|
//https://en.wikipedia.org/wiki/Optical_depth
|
|
#ifdef ATTEN_METHOD_1
|
|
Attenuation = exp(-d)*UnityScaleMatch;
|
|
#endif
|
|
|
|
#ifdef ATTEN_METHOD_2
|
|
Attenuation = UnityScaleMatch / (4 * PI*d*d);
|
|
#endif
|
|
|
|
//Linear
|
|
#ifdef ATTEN_METHOD_3
|
|
Attenuation = saturate(1 - d / UnityScaleMatch);
|
|
|
|
|
|
#endif
|
|
return float4(Attenuation * LightAttenuation * color, Attenuation* LightAttenuation);
|
|
}
|
|
|
|
float4 SpotLight(float3 LightPosition, float3 VolumePosition,
|
|
half size, half3 color, half LightAttenuation, float4 Direction, half Angle, half fallof, v2f i)
|
|
{
|
|
float3 spotDir = normalize(Direction.xyz);
|
|
Angle *= .5;//Unity match
|
|
float coneAngle = Angle;//inner angle
|
|
float coneDelta = Angle + fallof;//outer angle
|
|
|
|
float3 lray = normalize(LightPosition - VolumePosition);
|
|
float SpotCone = (dot(lray, -spotDir) - cos(radians(coneDelta))) / (cos(radians(coneAngle)) - cos(radians(coneDelta)));
|
|
SpotCone = max(0, SpotCone);
|
|
float4 _PointLight = PointLight(LightPosition, VolumePosition,
|
|
size, color, LightAttenuation, i);
|
|
|
|
return SpotCone * _PointLight;
|
|
// half d = distance(LightPosition, VolumePosition) / size;
|
|
// float Attenuation = 0;
|
|
// half UnityScaleMatch = 5;
|
|
// //https://en.wikipedia.org/wiki/Optical_depth
|
|
//#ifdef ATTEN_METHOD_1
|
|
// Attenuation = exp(-d)*UnityScaleMatch;
|
|
//#endif
|
|
//
|
|
//#ifdef ATTEN_METHOD_2
|
|
// Attenuation = UnityScaleMatch / (4 * PI*d*d);
|
|
//#endif
|
|
//
|
|
// //Linear
|
|
//#ifdef ATTEN_METHOD_3
|
|
// Attenuation = saturate(1 - d / UnityScaleMatch);
|
|
//
|
|
//
|
|
//#endif
|
|
// return float4(Attenuation * SpotCone * LightAttenuation * color, Attenuation* LightAttenuation);
|
|
}
|
|
|
|
|
|
#if UNITY_LIGHT_PROBE_PROXY_VOLUME
|
|
|
|
// normal should be normalized, w=1.0
|
|
half3 FVSHEvalLinearL0L1_SampleProbeVolume(half4 normal, float3 worldPos)
|
|
{
|
|
const float transformToLocal = unity_ProbeVolumeParams.y;
|
|
const float texelSizeX = unity_ProbeVolumeParams.z;
|
|
|
|
//The SH coefficients textures and probe occlusion are packed into 1 atlas.
|
|
//-------------------------
|
|
//| ShR | ShG | ShB | Occ |
|
|
//-------------------------
|
|
|
|
float3 position = (transformToLocal == 1.0f) ? mul(unity_ProbeVolumeWorldToObject, float4(worldPos, 1.0)).xyz : worldPos;
|
|
float3 texCoord = (position - unity_ProbeVolumeMin.xyz) * unity_ProbeVolumeSizeInv.xyz;
|
|
texCoord.x = texCoord.x * 0.25f;
|
|
|
|
// We need to compute proper X coordinate to sample.
|
|
// Clamp the coordinate otherwize we'll have leaking between RGB coefficients
|
|
float texCoordX = clamp(texCoord.x, 0.5f * texelSizeX, 0.25f - 0.5f * texelSizeX);
|
|
|
|
// sampler state comes from SHr (all SH textures share the same sampler)
|
|
texCoord.x = texCoordX;
|
|
half4 SHAr = UNITY_SAMPLE_TEX3D_SAMPLER_LOD(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord, 0);
|
|
|
|
texCoord.x = texCoordX + 0.25f;
|
|
half4 SHAg = UNITY_SAMPLE_TEX3D_SAMPLER_LOD(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord, 0);
|
|
|
|
texCoord.x = texCoordX + 0.5f;
|
|
half4 SHAb = UNITY_SAMPLE_TEX3D_SAMPLER_LOD(unity_ProbeVolumeSH, unity_ProbeVolumeSH, texCoord, 0);
|
|
|
|
// Linear + constant polynomial terms
|
|
half3 x1;
|
|
x1.r = dot(SHAr, normal);
|
|
x1.g = dot(SHAg, normal);
|
|
x1.b = dot(SHAb, normal);
|
|
|
|
return x1;
|
|
}
|
|
#endif
|
|
|
|
half3 ShadeSHPerPixel(half3 normal, half3 ambient, float3 worldPos)
|
|
{
|
|
half3 ambient_contrib = 0.0;
|
|
|
|
// L2 per-vertex, L0..L1 & gamma-correction per-pixel
|
|
// Ambient in this case is expected to be always Linear, see ShadeSHPerVertex()
|
|
#if UNITY_LIGHT_PROBE_PROXY_VOLUME
|
|
if (unity_ProbeVolumeParams.x == 1.0)
|
|
ambient_contrib = FVSHEvalLinearL0L1_SampleProbeVolume(half4(normal, 1.0), worldPos);
|
|
else
|
|
ambient_contrib = SHEvalLinearL0L1(half4(normal, 1.0));
|
|
#else
|
|
ambient_contrib = 1;// SHEvalLinearL0L1(half4(normal, 1.0));
|
|
#endif
|
|
|
|
ambient = max(half3(0, 0, 0), ambient + ambient_contrib); // include L2 contribution in vertex shader before clamp.
|
|
#ifdef UNITY_COLORSPACE_GAMMA
|
|
ambient = LinearToGammaSpace(ambient);
|
|
#endif
|
|
|
|
|
|
return ambient;
|
|
}
|
|
#endif
|