http://www.youtube.com/v/WdwJ2WvjY_A
In this post I am going to show you how to do multiple passes, how to make your handy HLSL function calls available to more than one shader at a time, how to pass variables to pixel and vertex shaders (in the hlsl pass) while creating a simple planet shader with these elements.
This tutorial seems to have grown and grown, the code for this post I wrote ages ago, but as I am sure you know by my lack of post, I have been very busy and not had much time to write it up and get it posted. Most of my time has been centred around work and family, and some time on a game I am trying to write with the hope of getting it on the XBLCG, I am aiming to get it into peer review by December, more on that later, back to this post…. So I have had to try and remember what I was thinking as I wrote some of the elements to this, some of it I remember clearly, others not so.
This post is mostly going to try and explain how I came up with my planet shader (see clip) it’s not perfect, but I had fun writing it and I hope that it gives you some ideas for your shaders.
Now this shader did not start off with the intention of being a multi pass shader, but once I got to adding more and more bits to it I found I started to run out of instruction slots and the only way around it that I could think of was to break the shader into more than one pass. So two passes went to three, three went to four and finally four to five.
How do we go about creating a multi-pass shader? Well that’s quite simple, you just have more than one pass in your technique. The initial issue that I had was that my last technique over wrote the one before it so I discovered that I needed to blend the passes. This is done like any other blending the only difference is that it has to be done in the pass it’s self. Some passes needed an alpha blend others an additive blend.
I am not going to go into too much detail on the code in the pixel and vertex shaders as it’s mostly covered in the previous episodes, I’ll mostly cover the passes.
First Pass
In this pass we just do the colour, bump, glow and reflection shading giving us a basic planet, later when we add the clouds we will go back to the colour pixel shader we will add the shadows to this pass for the clouds. So, our first pass looks like this:
pass Colors
{
AlphaBlendEnable = False;
CullMode = CCW;
VertexShader = compile vs_2_0 VS_Color();
PixelShader = compile ps_2_0 PS_Color();
}
As you can see I am setting some render states in this pass. I am just making sure the alpha blending is off and that the cull mode is counter clock wise, then the regular vertex shader is called followed by the pixel shader.

Second Pass
In the second pass I want to add some ripples to the oceans. The geometry is already drawn in the first pass so all I need to do is call my shader to apply the waves to the oceans, bus as I have said above, I need to blend the two pixel shaders to get my effect.
pass Waves
{
AlphaBlendEnable = True;
SrcBlend = SrcAlpha;
DestBlend = One;
PixelShader = compile ps_2_0 PS_Water();
}
So, we switch on alpha blending, then do an additive blend. I can then call my water pixel shader to apply the ripples. If you look realy close, the ripples move across the surface of the planet, I have a function that alters the text coords of the ripples to do this. To show that you can share functions across shaders I put this function in another file, how this is accessed I will cover later.
Third Pass
In this pass I want to add my clouds, to do this I am going to draw the geometry again, this time slightly larger than the planets surface, that way my clouds will appear a little raised from plants surface. I also wanted so shadows to appear on the surface of the planet cast by the clouds. To do this I went back to the cloud pixel shader and added the cloud textures to the final colour calculation but rendered them based on a shadow intensity. To “animate” the clouds I rotate the texture around the planet, again there is a shader parameter you can use to control the speed and I have a function to move the text coords of the cloud texture to do this and again, this is in my seperate function file.
pass Clouds
{
// Already set, no need to set again, keep them incase I move the passes about though.
//AlphaBlendEnable = True;
//SrcBlend = SrcAlpha;
//DestBlend = One;
VertexShader = compile vs_2_0 VS_OuterAtmoshpere(.02);
PixelShader = compile ps_2_0 PS_Cloud();
}
As you can see the vertex shader here is passed a parameter, this parameter is how much bigger I want to draw the new geometry. The blending is set in the previous pass so no need to set it again, I have kept the code there though in case I decide to move it.
VS_OUT2 VS_OuterAtmoshpere(VS_IN input,uniform float size)
{
VS_OUT2 output = (VS_OUT2)0;
output.Normal = mul(input.Normal, world);
output.Position = mul(input.Position, wvp) + (mul(size, mul(input.Normal, wvp)));
output.TexCoord = input.TexCoord;
output.pos = mul(input.Position,world);
return output;
}
As you can see, you need to declare the size parameter as a uniform in order to have it as an internal parameter. As you can see the position the current vertex is draw at is moved out along the direction of the normal by the distance of the size passed in, so drawing me a larger model around the original.
Forth Pass
In this pass I attempt to fake atmosphere, I don’t need to render the geometry again as I can use the geometry the cloud pass has just created, this time though I use an alpha blend to get the effect I want.
pass OuterAtmoshpere
{
//AlphaBlendEnable = True;
//SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
// No need to move it out again, can use the same geom as the clouds.
//VertexShader = compile vs_2_0 VS_OuterAtmoshpere(.02);
PixelShader = compile ps_2_0 PS_OuterAtmoshpere(true);
}
You can see in the pixel shader I pass a Boolean, this is so I only had to write one pixel shader for both the inner atmosphere and the atmosphere halo.
Fifth Pass
So, onto the final pass now, the atmosphere halo. This works pretty much the same as the previous pass, only we set the cull mode to be clock wise. This results in the the sphere being rendered inside out, I also render the sphere much larger.
pass UpperOuterAtmoshpere
{
//AlphaBlendEnable = True;
//SrcBlend = SrcAlpha;
//DestBlend = InvSrcAlpha;
VertexShader = compile vs_2_0 VS_OuterAtmoshpere(.2);
PixelShader = compile ps_2_0 PS_OuterAtmoshpere(false);
CullMode = CW;
}
In the class used to render the planet, I make sure that the cull mode is set back to counter clock wise cull after the render.
Shader Header
This is where I have put the functions to rotate and to revolve the texture coordinates, I did this to show how you don’t have to keep writing the same functions in each of your shaders if they are common across your shaders. So to have access to these functions in my shader I just do this (if you are a C/C++ programmer this will look very familiar :P)
#include "ShaderTools.fxh"
Simple as that….
You can find the solution for this post here.