440 lines
13 KiB
HLSL
440 lines
13 KiB
HLSL
// Set by the asset automatically
|
|
#define FORCE_GLES_COMPATIBILITY 0
|
|
#define LIQUID_VOLUME_SCATTERING
|
|
#define LIQUID_VOLUME_SMOKE
|
|
#define LIQUID_VOLUME_BUBBLES
|
|
#define LIQUID_VOLUME_FP_RENDER_TEXTURES
|
|
//#define LIQUID_VOLUME_ORTHO
|
|
#define LIQUID_VOLUME_USE_NOISE3D
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareOpaqueTexture.hlsl"
|
|
|
|
|
|
struct appdata_base {
|
|
float4 vertex : POSITION;
|
|
float3 normalOS : NORMAL;
|
|
float4 tangentOS : TANGENT;
|
|
float4 texcoord : TEXCOORD0;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
};
|
|
|
|
struct Input {
|
|
float4 positionCS : SV_POSITION;
|
|
float3 realPos : TEXCOORD0;
|
|
float4 vertex : TEXCOORD1;
|
|
float3 camPos : TEXCOORD2;
|
|
float4 screenPos : TEXCOORD3;
|
|
float3 normalWS : TEXCOORD5;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
struct SurfaceOutput {
|
|
half3 Albedo; // diffuse color
|
|
half3 Normal; // tangent space normal, if written
|
|
half3 Emission;
|
|
half Specular; // specular power in 0..1 range
|
|
half Gloss; // specular intensity
|
|
half Alpha; // alpha for transparencies
|
|
};
|
|
|
|
sampler2D _NoiseTex2D;
|
|
float _DepthAwareOffset;
|
|
|
|
#if LIQUID_VOLUME_DEPTH_AWARE_PASS
|
|
TEXTURE2D_X_FLOAT(_VLFrontBufferTexture);
|
|
#endif
|
|
|
|
#if LIQUID_VOLUME_IRREGULAR
|
|
TEXTURE2D_X_FLOAT(_VLBackBufferTexture);
|
|
#endif
|
|
|
|
float3 _VertexOffset;
|
|
half4 _Color1;
|
|
half4 _Color2;
|
|
half4 _FoamColor;
|
|
float _FoamMaxPos;
|
|
int _FoamRaySteps;
|
|
float _FoamDensity;
|
|
float _FoamBottom;
|
|
float _FoamTurbulence;
|
|
float _LevelPos;
|
|
float3 _FlaskThickness;
|
|
float4 _Size;
|
|
float3 _Center;
|
|
float _Muddy;
|
|
float4 _Turbulence;
|
|
float _DeepAtten;
|
|
half4 _SmokeColor;
|
|
float _SmokeAtten;
|
|
int _SmokeRaySteps;
|
|
float _SmokeSpeed;
|
|
float _SmokeHeightAtten;
|
|
int _LiquidRaySteps;
|
|
half4 _GlossinessInt;
|
|
float _FoamWeight;
|
|
float4 _Scale;
|
|
float _UpperLimit;
|
|
float _LowerLimit;
|
|
float3 wsCameraPos;
|
|
half3 _EmissionColor;
|
|
float _DoubleSidedBias;
|
|
float _BackDepthBias;
|
|
|
|
float4x4 _Rot;
|
|
float _TurbulenceSpeed;
|
|
|
|
float _FlaskBlurIntensity;
|
|
|
|
half _Glossiness;
|
|
half4 _FlaskTint;
|
|
half4 _FlaskColor;
|
|
half3 _LightColor;
|
|
|
|
#if defined(LIQUID_VOLUME_SCATTERING)
|
|
float4 _PointLightPosition[6];
|
|
half4 _PointLightColor[6];
|
|
float _PointLightInsideAtten;
|
|
#endif
|
|
|
|
int _LVForcedInvisible;
|
|
|
|
// Open GL 2.0 / WebGL support
|
|
#if SHADER_API_GLES || FORCE_GLES_COMPATIBILITY
|
|
#define WEBGL_OR_LEGACY
|
|
#endif
|
|
|
|
#if defined(WEBGL_OR_LEGACY)
|
|
sampler2D _Noise2Tex;
|
|
|
|
inline float noise3D(float3 x) {
|
|
x *= 100.0;
|
|
float3 f = frac(x);
|
|
float3 p = x - f;
|
|
f = f*f*(3.0-2.0*f);
|
|
float4 xy = float4(p.xy + float2(37.0,17.0)*p.z + f.xy, 0, 0);
|
|
xy.xy = (xy.xy + 0.5) / 256.0;
|
|
float2 zz = tex2Dlod(_Noise2Tex, xy).yx;
|
|
return lerp( zz.x, zz.y, f.z );
|
|
}
|
|
#define SAMPLE_NOISE_3D(tex,pos) noise3D( (pos).xyz)
|
|
#define BEGIN_LOOP(k,iterations,defaultIterations) for(int k=0;k<defaultIterations;k++) if (k<iterations) {
|
|
#define END_LOOP }
|
|
#else
|
|
sampler3D _NoiseTex;
|
|
#define SAMPLE_NOISE_3D(tex,pos) tex3Dlod(tex,pos)
|
|
#define BEGIN_LOOP(k,iterations,defaultIterations) for(int k=0;k<iterations;k++) {
|
|
#define END_LOOP }
|
|
#endif
|
|
|
|
#if !defined(LIQUID_VOLUME_USE_NOISE3D)
|
|
#undef SAMPLE_NOISE_3D
|
|
#define SAMPLE_NOISE_3D(x,y) 0.5
|
|
#endif
|
|
|
|
#if LIQUID_VOLUME_USE_REFRACTION
|
|
half4 LightingWrappedSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
|
|
half diff = saturate(lightDir.y * 2.0);
|
|
half3 h = normalize (lightDir + viewDir);
|
|
half nh = saturate (dot (s.Normal, h));
|
|
half spec = pow (nh, _GlossinessInt.x);
|
|
half4 c;
|
|
|
|
// apply light scattering
|
|
#if defined(LIQUID_VOLUME_SCATTERING)
|
|
half sunDiffIntensity = pow( max( dot( viewDir, -lightDir), 0.0 ), _GlossinessInt.y) * _GlossinessInt.z;
|
|
diff += sunDiffIntensity;
|
|
#endif
|
|
|
|
half fresnelTerm = Pow4(1.0 - saturate(dot(s.Normal, viewDir)));
|
|
spec += fresnelTerm * _GlossinessInt.w;
|
|
|
|
half3 ambient = SampleSH(s.Normal);
|
|
|
|
c.rgb = (s.Albedo * _LightColor * diff + _LightColor * spec * diff + ambient * fresnelTerm) * atten + s.Emission;
|
|
c.a = s.Alpha;
|
|
return c;
|
|
}
|
|
#else
|
|
half4 LightingWrappedSpecular (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
|
|
half diff = saturate(lightDir.y * 2.0);
|
|
half4 c;
|
|
|
|
// apply light scattering
|
|
#if defined(LIQUID_VOLUME_SCATTERING)
|
|
half sunDiffIntensity = pow( max( dot( viewDir, -lightDir), 0.0 ), _GlossinessInt.y) * _GlossinessInt.z;
|
|
diff += sunDiffIntensity;
|
|
#endif
|
|
|
|
half fresnelTerm = Pow4(1.0 - saturate(dot(s.Normal, viewDir)));
|
|
half3 ambient = SampleSH(s.Normal);
|
|
|
|
c.rgb = (s.Albedo * _LightColor * diff + ambient * fresnelTerm) * atten + s.Emission;
|
|
c.a = s.Alpha;
|
|
return c;
|
|
}
|
|
#endif
|
|
|
|
half4 LightingSimple (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
|
|
half4 c;
|
|
c.rgb = s.Albedo * _LightColor * atten + s.Emission;
|
|
c.a = s.Alpha;
|
|
return c;
|
|
}
|
|
|
|
void intSphere(float3 rd, out float t0, out float t1) {
|
|
float3 d = wsCameraPos - _Center;
|
|
float b = dot(rd, d);
|
|
float c = dot(d,d) - _Size.w * _Size.w;
|
|
float t = sqrt(b*b - c);
|
|
t0 = -b-t;
|
|
t1 = -b+t;
|
|
}
|
|
|
|
void intCylinder(float3 rd, out float t0, out float t1) {
|
|
#if LIQUID_VOLUME_NON_AABB
|
|
rd = mul((float3x3)_Rot, rd);
|
|
#endif
|
|
float3 d = wsCameraPos - _Center;
|
|
#if LIQUID_VOLUME_NON_AABB
|
|
d = mul((float3x3)_Rot, d);
|
|
#endif
|
|
float a = dot(rd.xz, rd.xz);
|
|
float b = dot(rd.xz, d.xz);
|
|
float c = dot(d.xz,d.xz) - _Size.w * _Size.w;
|
|
float t = sqrt(max(b*b-a*c,0)); // max prevents artifacts with MSAA
|
|
t0 = (-b-t)/a;
|
|
t1 = (-b+t)/a;
|
|
|
|
// cylinder cap
|
|
float sy = _Size.y * 0.5 * _FlaskThickness.y;
|
|
float h = abs(d.y) - sy;
|
|
if (h>0) {
|
|
float rdl = dot(rd.xz / rd.y, rd.xz / rd.y);
|
|
float tc = h * sqrt (1.0 + rdl);
|
|
t0 = max(t0, tc);
|
|
}
|
|
|
|
h = sign(rd.y) * -d.y + sy;
|
|
if (h>0) {
|
|
float rdl = dot(rd.xz / rd.y, rd.xz / rd.y);
|
|
float tc = h * sqrt (1.0 + rdl);
|
|
t1 = min(t1, tc);
|
|
}
|
|
}
|
|
|
|
void intBox(float3 rd, out float t0, out float t1) {
|
|
#if LIQUID_VOLUME_NON_AABB
|
|
rd = mul((float3x3)_Rot, rd);
|
|
#endif
|
|
float3 ro = wsCameraPos - _Center;
|
|
#if LIQUID_VOLUME_NON_AABB
|
|
ro = mul((float3x3)_Rot, ro);
|
|
#endif
|
|
|
|
float3 invR = 1.0 / rd;
|
|
float3 boxmin = - _Size.w;
|
|
float3 boxmax = + _Size.w;
|
|
float3 tbot = invR * (boxmin - ro);
|
|
float3 ttop = invR * (boxmax - ro);
|
|
float3 tmin = min (ttop, tbot);
|
|
float3 tmax = max (ttop, tbot);
|
|
float2 tt0 = max (tmin.xx, tmin.yz);
|
|
t0 = max(tt0.x, tt0.y);
|
|
tt0 = min (tmax.xx, tmax.yz);
|
|
t1 = min(tt0.x, tt0.y);
|
|
}
|
|
|
|
|
|
void vert(appdata_base v, out Input o) {
|
|
o = (Input)0;
|
|
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
UNITY_TRANSFER_INSTANCE_ID(v, o);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
|
|
|
o.vertex = v.vertex;
|
|
o.vertex.w = dot(o.vertex.xz, _Turbulence.zw) + _TurbulenceSpeed;
|
|
o.vertex.xz *= 0.1.xx * _Turbulence.xx; // extracted from frag
|
|
o.vertex.xz += _Time.xx;
|
|
v.vertex.xyz *= _FlaskThickness;
|
|
|
|
VertexPositionInputs vertexInput = GetVertexPositionInputs(v.vertex.xyz);
|
|
VertexNormalInputs normalInput = GetVertexNormalInputs(v.normalOS, v.tangentOS);
|
|
o.normalWS = normalInput.normalWS;
|
|
o.realPos = vertexInput.positionWS;
|
|
#if LIQUID_VOLUME_IGNORE_GRAVITY
|
|
o.realPos = mul((float3x3)_Rot, o.realPos - _Center) + _Center;
|
|
o.camPos = mul((float3x3)_Rot, _WorldSpaceCameraPos - _Center) + _Center;
|
|
#else
|
|
o.camPos = _WorldSpaceCameraPos;
|
|
#endif
|
|
|
|
o.positionCS = vertexInput.positionCS;
|
|
|
|
#if LIQUID_VOLUME_IRREGULAR
|
|
if (_LVForcedInvisible == 1) {
|
|
o.positionCS.xy = -10000;
|
|
}
|
|
#endif
|
|
|
|
o.screenPos = ComputeScreenPos(o.positionCS);
|
|
}
|
|
|
|
half4 raymarch(float4 vertex, float3 rd, float t0, float t1); // forward declaration
|
|
|
|
SAMPLER(sampler_point_clamp);
|
|
#if defined(LIQUID_VOLUME_FP_RENDER_TEXTURES)
|
|
#define texDistance(tex, uv) SAMPLE_TEXTURE2D_X(tex, sampler_point_clamp, uv).r
|
|
#else
|
|
|
|
inline float DecodeFloatRGBA( float4 enc ) {
|
|
float4 kDecodeDot = float4(1.0, 1/255.0, 1/65025.0, 1/16581375.0);
|
|
return dot( enc, kDecodeDot );
|
|
}
|
|
|
|
#define texDistance(tex, uv) ( (1.0 / DecodeFloatRGBA(SAMPLE_TEXTURE2D_X(tex, sampler_point_clamp, uv))) - 1.0 )
|
|
#endif
|
|
|
|
|
|
float minimum_distance_sqr(float rayLengthSqr, float3 w, float3 p) {
|
|
// Return minimum distance between line segment vw and point p
|
|
float t = saturate(dot(p, w) / rayLengthSqr);
|
|
float3 projection = t * w;
|
|
return dot(p - projection, p - projection);
|
|
}
|
|
|
|
inline float3 ProjectOnPlane(float3 v, float3 planeNormal) {
|
|
float sqrMag = dot(planeNormal, planeNormal);
|
|
float dt = dot(v, planeNormal);
|
|
return v - planeNormal * dt / sqrMag;
|
|
}
|
|
|
|
float GetLightAttenuation(float3 wpos) {
|
|
float4 shadowCoord = TransformWorldToShadowCoord(wpos);
|
|
float atten = MainLightRealtimeShadow(shadowCoord);
|
|
return atten;
|
|
}
|
|
|
|
half3 boxBlur(float2 uv) {
|
|
const int BOXRADIUS = 5;
|
|
int kernel_window_size = BOXRADIUS*2+1;
|
|
|
|
half3 color = 0.0.xxx;
|
|
UNITY_UNROLL
|
|
for (int ry = -BOXRADIUS; ry <= BOXRADIUS; ++ry) {
|
|
UNITY_UNROLL
|
|
for (int rx = -BOXRADIUS; rx <= BOXRADIUS; ++rx) {
|
|
color += SampleSceneColor(uv + float2(rx,ry)/_ScreenParams.xy);
|
|
}
|
|
}
|
|
|
|
half w = (BOXRADIUS * 2 + 1) * (BOXRADIUS * 2 + 1);
|
|
return (color/w);
|
|
}
|
|
|
|
|
|
half4 frag (Input IN) : SV_Target {
|
|
|
|
UNITY_SETUP_INSTANCE_ID(IN);
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(IN);
|
|
|
|
if (IN.vertex.y > _UpperLimit || IN.vertex.y < _LowerLimit) return 0.0.xxxx;
|
|
|
|
wsCameraPos = IN.camPos;
|
|
|
|
#if defined(LIQUID_VOLUME_ORTHO)
|
|
float3 cameraForward = UNITY_MATRIX_V[2].xyz;
|
|
float3 orthoCameraPos = ProjectOnPlane(IN.realPos - wsCameraPos, cameraForward) + wsCameraPos;
|
|
wsCameraPos = lerp(wsCameraPos, orthoCameraPos, unity_OrthoParams.w);
|
|
#endif
|
|
|
|
float t0, t1;
|
|
float3 rd = IN.realPos - wsCameraPos;
|
|
float dist = length(rd);
|
|
rd /= dist;
|
|
|
|
#if LIQUID_VOLUME_SPHERE
|
|
intSphere(rd, t0, t1);
|
|
#elif LIQUID_VOLUME_CUBE
|
|
intBox(rd, t0, t1);
|
|
#elif LIQUID_VOLUME_CYLINDER
|
|
intCylinder(rd, t0, t1);
|
|
#else
|
|
t0 = dist;
|
|
t1 = dist + _Size.x + _BackDepthBias;
|
|
#endif
|
|
|
|
t0 = max(0,t0); // needed if camera is inside container
|
|
|
|
#if LIQUID_VOLUME_DEPTH_AWARE || LIQUID_VOLUME_DEPTH_AWARE_PASS || LIQUID_VOLUME_IRREGULAR
|
|
float2 uv = IN.screenPos.xy / IN.screenPos.w;
|
|
#endif
|
|
|
|
#if LIQUID_VOLUME_DEPTH_AWARE
|
|
#if defined(LIQUID_VOLUME_ORTHO)
|
|
float vz = SampleSceneDepth(uv);
|
|
#if UNITY_REVERSED_Z
|
|
vz = 1.0 - vz;
|
|
#endif
|
|
float z = vz * _ProjectionParams.z;
|
|
#else
|
|
float depth = SampleSceneDepth(uv);
|
|
float4 raw = mul(UNITY_MATRIX_I_VP, float4(uv * 2.0 - 1.0, depth, 1.0));
|
|
float3 wpos = raw.xyz / raw.w;
|
|
float z = distance(wsCameraPos, wpos.xyz) + _DepthAwareOffset;
|
|
#endif
|
|
t1 = min(t1, z);
|
|
#endif
|
|
|
|
#if LIQUID_VOLUME_DEPTH_AWARE_PASS
|
|
float frontDist = texDistance(_VLFrontBufferTexture, UnityStereoTransformScreenSpaceTex(uv));
|
|
t1 = min(t1, frontDist);
|
|
#endif
|
|
|
|
#if LIQUID_VOLUME_IRREGULAR
|
|
float backDist = texDistance(_VLBackBufferTexture, UnityStereoTransformScreenSpaceTex(uv));
|
|
t1 = min(t1, backDist);
|
|
clip(t1-t0-_DoubleSidedBias);
|
|
#endif
|
|
|
|
half4 co = raymarch(IN.vertex,rd,t0,t1);
|
|
|
|
#if defined(LIQUID_VOLUME_SCATTERING)
|
|
float3 rayStart = wsCameraPos + rd * t0;
|
|
float rayLength = t1 - t0;
|
|
rayStart += rd * _PointLightInsideAtten;
|
|
rayLength -= _PointLightInsideAtten;
|
|
float3 ray = rd * rayLength;
|
|
float rayLengthSqr = rayLength * rayLength;
|
|
for (int k=0;k<6;k++) {
|
|
half pointLightInfluence = minimum_distance_sqr(rayLengthSqr, ray, _PointLightPosition[k].xyz - rayStart) / _PointLightColor[k].w;
|
|
co.rgb += _PointLightColor[k].rgb * (co.a / (1.0 + pointLightInfluence));
|
|
}
|
|
#endif
|
|
|
|
SurfaceOutput s = (SurfaceOutput)0;
|
|
s.Albedo = co.rgb;
|
|
s.Emission = co.rgb * _EmissionColor;
|
|
s.Alpha = co.a;
|
|
s.Normal = IN.normalWS;
|
|
|
|
half atten = 1.0; //GetLightAttenuation(IN.realPos); // uncomment if shadow support is required
|
|
co = LIGHTING(s, _MainLightPosition.xyz, -rd, atten);
|
|
|
|
#if !UNITY_COLORSPACE_GAMMA
|
|
co.rgb = SRGBToLinear(co.rgb);
|
|
#endif
|
|
|
|
#if LIQUID_VOLUME_USE_REFRACTION
|
|
float4 uvgrab = IN.screenPos;
|
|
half4 bgColor = half4(boxBlur(uvgrab.xy/uvgrab.w), _FlaskBlurIntensity);
|
|
co = lerp(bgColor, co, co.a);
|
|
#endif
|
|
|
|
return co;
|
|
|
|
}
|