Fluid Shader File

For anyone who wants to modify the shader to tweak the shading functionality, I will explain how the shading process works, though i personally not recommend.

The files used for fluid shading are in Assets/SSF_Particle2Fluid_ShaderUtil/Shaders/FluidRender.shader

The whole process can be divided into the following steps:

  • Read the surface parameters through the map:

TEXTURE2D(_ColorTex);
TEXTURE2D_FLOAT(_EyeDepthTex);
TEXTURE2D(_EyeNormalTex);
TEXTURE2D_FLOAT(_ParticleDataTex);
TEXTURE2D_FLOAT(_ThicknessTex);
StructuredBuffer<SSF_RenderParams> _RenderParams;

....
float fluidEyeDepth = SAMPLE_TEXTURE2D(_EyeDepthTex, sampler_PointClamp, uv).r;
float3 fluidColor = SAMPLE_TEXTURE2D(_ColorTex, sampler_LinearRepeat, uv).xyz;
float thickness = SAMPLE_TEXTURE2D(_ThicknessTex, sampler_LinearRepeat, uv).r;
float3 fluidEyeNormal = SAMPLE_TEXTURE2D(_EyeNormalTex, sampler_LinearRepeat, uv).xyz;
int paramIndex = SAMPLE_TEXTURE2D(_ParticleDataTex, sampler_PointClamp,uv).x;
float smoothness = _RenderParams[paramIndex].smoothness;
float refractiveIndex = _RenderParams[paramIndex].ior;
float fresnelPower = _RenderParams[paramIndex].fresnelPower;
float minReflectionRatio = _RenderParams[paramIndex].minReflectionRatio;
float maxReflectionRatio = _RenderParams[paramIndex].maxReflectionRatio;
float isTransparent = _RenderParams[paramIndex].isTransparent;
float4 ambientColor = _RenderParams[paramIndex].ambientColor;
float4 diffuseColor = _RenderParams[paramIndex].diffuseColor;
float4 specularColor = _RenderParams[paramIndex].specularColor;
  • We first filter out the pixels that are not blocked by the fluid particles and return them to the previously rendered color.

half4 color = SAMPLE_TEXTURE2D_X_LOD(_BlitTexture, sampler_LinearRepeat, uv, _BlitMipLevel);
#if UNITY_REVERSED_Z
real depth = SampleSceneDepth(uv);
#else
// Adjust z to match NDC for OpenGL
real depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(UV));
#endif
float sceneEyeDepth = LinearEyeDepth(depth, _ZBufferParams);
       	if(fluidEyeDepth <= 0.0f || fluidEyeDepth > 1000.0f ||sceneEyeDepth < fluidEyeDepth) {
	return color;
}

Based on the above parameters, you can play with the shader to create different effects.

  • For example, I first made a blinn-phong rendering

  DirectionalLightData mainLight = _DirectionalLightDatas[0];
  float3 lightColor = mainLight.color;
  float3 lightDir = mainLight.forward;

  //blinn-phong
  float NdotL = saturate(dot(fluidEyeNormal,lightDir));
  lightColor = min(lightColor,1);
  float3 diffuse = lightColor * NdotL * params.diffuseColor.rgb;

  float3 halfVec = SafeNormalize(float3(lightDir) + float3(viewDir));
  half NdotH = half(saturate(dot(fluidEyeNormal, halfVec)));
  half modifier = pow(float(NdotH), float(params.smoothness*64)); // Half produces banding, need full precision
  // NOTE: In order to fix internal compiler error on mobile platforms, this needs to be float3
  float3 specularReflection = params.specularColor.rgb * modifier;
  float3 specular = lightColor * specularReflection;

  float3 ambient = params.ambientColor.rgb ;
  float3 finalColorNonTransparent = diffuse+specular+ambient;
  • In addition, you can use parameters such as thickness, fluid color, IOR, smoothness, etc. to create water rendering effects that include refraction and reflection:

float eta = 1.0 / params.ior;
float F0 = ((1.0 - eta) * (1.0 - eta)) / ((1.0 + eta) * (1.0 + eta));
float fresnel = F0 + (1.0 - F0) * pow(saturate(1.0 - dot(viewDir, fluidEyeNormal)), params.fresnelPower);
fresnel = clamp(fresnel, params.minReflectionRatio, params.maxReflectionRatio);

// reflection
float3 reflectionDir = reflect(-viewDir, fluidEyeNormal);
// a misc to do ssr, hdrp's reflection is way too complex, so we just use a simple reflection
// modify here for any color you want
float3 reflectionColor = SampleCameraColor(uv  + reflectionDir.xy * thickness * 0.1,0).xyz;
// reflectionColor = reflectionColor * 0.5 + 0.5 *diffuse;
reflectionColor = 1;
// float3 reflectionColor = LOAD_TEXTURE2D_X(_CameraColorTex, uv + reflectionDir.xy * thickness * 0.1).xyz;

// refraction
float3 refractionDir = refract(-viewDir, fluidEyeNormal, eta);
float2 refractionUV = uv  + refractionDir.xy * thickness * 0.1;
float3 refractionColor = SampleCameraColor(refractionUV,0).xyz;

float3 attenuation = exp(-(1.0 - fluidColor) * thickness * 5.0);
refractionColor *= attenuation;

// blend with fresnel value.
float3 finalColorTransparent = lerp(refractionColor, reflectionColor, fresnel);

Of course, we can also implement shadow effects, but since Unity's API is not stable and open enough, we will consider updating it later.

Last updated