2D metaball liquid effect - how to feed output of one rendering pass as input to another shader
- by Guye Incognito
I'm attempting to make a shader for unity3d web project. I want to implement something like in the great answer by DMGregory in this question. in order to achieve a final look something like this..
Its metaballs with specular and shading.
The steps to make this shader are.
1. Convert the feathered blobs into a heightmap.
2. Generate a normalmap from the heightmap
3. Feed the normal map and height map into a standard unity shader, for instance transparent parallax specular.
I pretty much have all the pieces I need assembled but I am new to shaders and need help putting them together
I can generate a heightmap from the blobs using some fragment shader code I wrote (I'm just using the red channel here cus i dont know if you can access the brightness)
half4 frag (v2f i) : COLOR{
half4 texcol,finalColor;
texcol = tex2D (_MainTex, i.uv);
finalColor=_MyColor;
if(texcol.r<_botmcut)
{
finalColor.r= 0;
}
else if((texcol.r>_topcut))
{
finalColor.r= 0;
}
else
{
float r = _topcut-_botmcut;
float xpos = _topcut - texcol.r;
finalColor.r= (_botmcut + sqrt((xpos*xpos)-(r*r)))/_constant;
}
return finalColor;
}
turns these blobs..
into this heightmap
Also I've found some CG code that generates a normal map from a height map. The bit of code that makes the normal map from finite differences is here
void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = fixed3(0.5);
float3 normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
float me = tex2D(_HeightMap,IN.uv_MainTex).x;
float n = tex2D(_HeightMap,float2(IN.uv_MainTex.x,IN.uv_MainTex.y+1.0/_HeightmapDimY)).x;
float s = tex2D(_HeightMap,float2(IN.uv_MainTex.x,IN.uv_MainTex.y-1.0/_HeightmapDimY)).x;
float e = tex2D(_HeightMap,float2(IN.uv_MainTex.x-1.0/_HeightmapDimX,IN.uv_MainTex.y)).x;
float w = tex2D(_HeightMap,float2(IN.uv_MainTex.x+1.0/_HeightmapDimX,IN.uv_MainTex.y)).x;
float3 norm = normal;
float3 temp = norm; //a temporary vector that is not parallel to norm
if(norm.x==1)
temp.y+=0.5;
else
temp.x+=0.5;
//form a basis with norm being one of the axes:
float3 perp1 = normalize(cross(norm,temp));
float3 perp2 = normalize(cross(norm,perp1));
//use the basis to move the normal in its own space by the offset
float3 normalOffset = -_HeightmapStrength * ( ( (n-me) - (s-me) ) * perp1 + ( ( e - me ) - ( w - me ) ) * perp2 );
norm += normalOffset;
norm = normalize(norm);
o.Normal = norm;
}
Also here is the built-in transparent parallax specular shader for unity.
Shader "Transparent/Parallax Specular" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.078125
_Parallax ("Height", Range (0.005, 0.08)) = 0.02
_MainTex ("Base (RGB) TransGloss (A)", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
_ParallaxMap ("Heightmap (A)", 2D) = "black" {}
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 600
CGPROGRAM
#pragma surface surf BlinnPhong alpha
#pragma exclude_renderers flash
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _ParallaxMap;
fixed4 _Color;
half _Shininess;
float _Parallax;
struct Input {
float2 uv_MainTex;
float2 uv_BumpMap;
float3 viewDir;
};
void surf (Input IN, inout SurfaceOutput o) {
half h = tex2D (_ParallaxMap, IN.uv_BumpMap).w;
float2 offset = ParallaxOffset (h, _Parallax, IN.viewDir);
IN.uv_MainTex += offset;
IN.uv_BumpMap += offset;
fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = tex.rgb * _Color.rgb;
o.Gloss = tex.a;
o.Alpha = tex.a * _Color.a;
o.Specular = _Shininess;
o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
}
FallBack "Transparent/Bumped Specular"
}