Making Organic Brain Chunks in Knit Worth Using Shaders

A lot of times in game dev, you put a lot of effort into a very specific thing that ultimately goes unnoticed by players.

Why?

Because if that thing is not there, then players will definitely notice its absence.

That very specific thing for us was the “blobby” effect of Granny's brain in Knit Worth.

In Knit Worth, you use crochet needles to poke and prod at a sweet old lady's brain chunks. Naturally, something like that should look soft and organic. Our solution was to use a simple vertex shader to manipulate the object on a sin() based rhythm. The result was something that looked like this:

blobbois.gif

For folks who don't exactly know how shaders work, essentially a shader file usually (though not always) contains two functions that manipulate whatever object it's attached to. The vertex shader function manipulates the position of a mesh's vertices, while a fragment shader manipulates the colors that are drawn to that object.

In the case of our blobby effect, we only needed to write a vertex shader. We weren't too concerned with changing the colors of the object fragments (a sacrifice that was made for the sake of time). Anyhoo, without further ado, here it is in all it's glory:

``` Shader "Custom/Pulsate" { Properties { _MainTex("Texture", 2D) = "white" {} _Color("Main Color", Color) = (1.0, 1.0, 1.0, 1.0) _Intensity("Intensity", Range(0, 0.1)) = 0.01 _Frequency("Frequency", Range(0, 100)) = 20 }

SubShader
{
    CGINCLUDE
    #include "UnityCG.cginc"

    struct appdata
    {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float2 uv : TEXCOORD0;
    };

    struct v2f
    {
        float2 uv : TEXCOORD0;
        float4 vertex : SV_POSITION;
        float3 normal : NORMAL;
    };

    sampler2D _MainTex;
    float4 _MainTex_ST;
    float _Intensity;
    float _Frequency;

    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.normal = normalize(mul(float4(v.normal, 0.0), unity_ObjectToWorld).xyz);
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);

        float4 newVertex = mul(v.vertex, unity_ObjectToWorld) + _Intensity * ((float4(o.normal, 0.0) * sin(o.uv.x * _Frequency + _Time.w)) + (float4(o.normal, 0.0) * sin(o.uv.y * _Frequency + _Time.w)));

        o.vertex = UnityObjectToClipPos(newVertex);

        return o;
    }

    ENDCG

    Pass 
    {
        ZWrite On
        ZTest LEqual

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        half4 frag(v2f i) : COLOR 
        {
            return tex2D(_MainTex, i.uv);
        }
        ENDCG
    }
}

} ```

It's not the cleanest or most optimized thing in the world, but it is functional. The main bulk of the heavy lifting is done in the vert() function. Essentially what I'm doing is oscillating a vertex's position along its normal using a sin() function. The vertex's final adjusted position depends on its texture UV coordinates as well as the Intensity and Frequency properties. These last 2 values can be manipulated in code or in the inspector to customize the blobby effect.

unityDemonstrationSmaller.gif

So there are a couple of fairly problematic issues with this shader that prevent it from being super robust.

Firstly, it does not handle seams well. If you look at the object too closely, or set the Intensity to be too high, you can see a break in the mesh that immediately breaks the blobby illusion.

Secondly, this shader does not handle lighting or normal/bump mapping in any way. That would require a lot of extra work in the fragment shader that we unfortunately just did not have the time to implement.

Despite these problems, we were able to create a halfway decent “organic” effect for any arbitrary object, including the little synapse connectors that wire up the inside of granny's head!

webBois.gif

It's a small little detail that required a super non-trivial amount of work, but we like to think that it looks a lot better than if it wasn't there at all!

If you're interested to see how it works in action, check it out at our game page!

https://ldjam.com/events/ludum-dare/44/knit-worth

And as always, thanks so much to everyone who has left feedback for us! We're working hard to make sure to return the favor for everyone who comments on our game, but it may take some time, so we appreciate your patience!