Skip to content
Tags

79. Shaders – creating mist and lighting up the dark

June 7, 2013

You can create some very cool effects with lighting using a shader, without getting into all the technicalities of lighting.

First, check out this code: https://gist.github.com/dermotbalson/5696603

You can walk around in this forest (instructions are in the output are at left), and adjust the level of mist with the parameter.

Then try turning the mist off with the boolean parameter. Now we are in darkness, holding a light, and the Visibility parameters controls how strong your light is. Again, you can walk around the forest.
http://instagram.com/p/aFw_L5hHcD/

These two effects come from virtually the same code. Remember, a little earlier, we found that if we reduce r,g,b and a together, we get a misty effect, but if we reduce r,g,b and keep a the same, our object goes dark instead. So we can flick between mist and dark very easily, depending on what we do with a.

But in both cases, the secret is in making the light (or mist) vary with distance.

Mist

In Codea first, I put this in draw, for each shader m.

    m.shader.dist=Visibility

Visibility is a Codea parameter which can vary between (say) 10 and 1000, and the idea is that you can’t see anything at a distance further than this.

Now I need to tell my shader where I am, so it can measure the distance to each object.

    m.shader.pos=vec2(posX,-posZ) --current position

Now, in my vertex shader, I make these changes

    uniform vec2 pos;
    uniform float dist;

    //and in the main function....
    float d=distance(pos,vec2(position.x,position.z));
    float j=clamp(1.-d/dist,0.,1.);
    vColor=color*j;

So we first define pos and dist so we can use them, then we calculate the distance between pos, and the current vertex position. We then calculate the colour reduction factor j so it is 0 if the distance is greater than dist, and 1 if the distance is 0. The clamp function simply makes sure the value stays between 0 and 1.

So in this case, we adjust r,g,b and a by the factor, giving a nice misty effect which gets stronger into the distance.

Darkness

Now I want to include the option of darkness, because I know it doesn’t require much change to the shader.

In Codea, I add a parameter called Mist that is true if mist is on, and false if we want darkness.

I also set a variable minLight=0.2, so we don’t go to complete blackness, as you will see.

In draw, I alter the background lighting, so if we are using darkness, it goes dark too.

    if Mist==true then background(220)
       else background(220*math.max(minLight,Visibility/1000)) end

I pass the minimum light level to the shader, as well as an indicator of whether we want mist or darkness.

    m.shader.minLight=minLight
    if Mist==true then m.shader.mist=1 else m.shader.mist=0 end

In the shader, I need to define the two new uniform variables, and modify the main code.

    uniform float mist;
    uniform float minLight;

    // and in main
    float d=distance(pos,vec2(position.x,position.z));
    float j=clamp(1.-d/dist,0.,1.);
    vec4 c=color;
    if (mist==0.) c.rgb = c.rgb*max(minLight,j);
    else c=c*j;
    vColor=c;

The code is the same until we have calculated the adjustment j. Then, if we want darkness, we need to adjust the rgb (but not a) of color, which means we need a new variable because color is read only. We also apply a minimum light level of minLight.

There is one more complication. The distance calculation relies on the fact that the vertex position holds the current location of the vertex. But if you have set the mesh up with x,z=0,0 (intending to translate to the actual position in the draw function), the vertex position in the shader will be 0,0, instead of its actual position. So we somehow have to pass through the actual x,z position in this case. We could do it with extra variables, but instead we can simply deduct that x,z from the camera position x,z, like so

    m.shader.pos=vec2(posX-m.pos.x,m.pos.z-posZ)

where m.pos.x and m.pos.z is the intended location of the current mesh. This isn’t ideal because it’s the x,z of the whole mesh, not the current vertex in that mesh, but it is close enough in most situations.

From → Shaders

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: