79. Shaders – creating mist and lighting up the dark
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.
Trackbacks & Pingbacks