So how exactly did I create my skybox?
First of all, I'm using a skysphere (however, I'll keep referring to it as a skybox anyway) - it looks a lot nicer. Though, the sphere is a pretty jagged one, it has only 72 faces.
Here is picture of the texture I used for the skybox:
And here is the CgFx shader code for displaying it:
float4x4 ModelViewProjXf;The skybox is a spherical polygon mesh that's loaded into the game engine, I then have a very specific effect (the above "Skybox.cgfx" shader), which I apply to that mesh. The shader does the texture mapping for the skybox by doing a look-up in a given cubemap. In the case of the "Deco" level of the game, the cube map has just one texture applied to all 6 faces of the cube. The "SkyCamPos" variable defines the center of the skybox - this is the location from which the 'rays' shoot out and sample the cubemap, giving the illusion that the skybox is very far away since it will always remain stationary to the viewer. I should note here that some skybox techniques like those used in Valve's Source engine allow for a very small amount of skybox movement as the viewer moves - I considered this but since the game is mostly stationary it's really not necessary.
float4x4 WorldXf;
float3 SkyCamPos;
float3 MultiplyColour <
> = {1.0f,1.0f,1.0f};
texture skyEnvMap : Environment <
string ResourceType = "Cube";
>;
samplerCUBE SkyboxSampler = sampler_state {
Texture = < skyEnvMap >;
WrapS = ClampToEdge;
WrapT = ClampToEdge;
WrapR = ClampToEdge;
};
// application to vertex shader -----------------
struct appdata {
float3 Position : POSITION;
};
// vertex shader to pixel shader ----------------
struct vertexOutput {
float4 HPosition : POSITION;
float3 WorldView : TEXCOORD0;
};
// Vertex Shader --------------------------------
vertexOutput skybox_VS(appdata IN) {
vertexOutput OUT = (vertexOutput)0;
float4 Pos = float4(IN.Position.xyz,1);
float3 Pw = mul(WorldXf,Pos).xyz;
OUT.HPosition = mul(ModelViewProjXf,Pos);
OUT.WorldView = normalize(Pw -
float3(SkyCamPos.x,SkyCamPos.y,SkyCamPos.z));
return OUT;
}
// Fragement Shader (Floating Point) ------------
float4 skybox_PS(vertexOutput IN) : COLOR {
// All we need to do is a look up in the cube map
// using the vector from the eye to the fragment...
float3 SkyColour = texCUBE(SkyboxSampler, IN.WorldView).xyz;
return float4(SkyColour * MultiplyColour, 1.0f);
}
technique Skybox
{
pass pass0
{
CullFaceEnable = true;
CullFace = Back;
DepthTestEnable = true;
DepthFunc = LEqual;
VertexProgram = compile vp40 skybox_VS();
FragmentProgram = compile fp40 skybox_PS();
}
}
The "MultiplyColour" variable is used to multiply the colour of the cubemap texel look-up with some specified colour in the game code. In the case of the "Deco" world, the colour is constantly shifting between a set of established colours. I do this with the following code:
// Figure out what the colour of the background should be...This code acts almost like a very simplified parametric spline - where each of the colours in the COLOUR_CHANGE_LIST array act as points in 3D space (RGB instead of XYZ). Each of the if-statements is responsible for shifting along the line that leads to the next colour point. If you look at the code really closely you'll notice that there are small jumps around the points since the COLOUR_CHANGE_SPEED is not a very small value (it's currently set to around 0.07), these jumps are impossible to see, however, since they too are still quite small.
double colourChangeInc = dT * COLOUR_CHANGE_SPEED;
Colour nextColour = COLOUR_CHANGE_LIST[
(this->currColourIndex + 1) % NUM_COLOUR_CHANGES];
bool transitionDone = true;
// Find out if there is a significant difference
// in each colour channel, if there is then move
// towards the next target colour in that channel
if (fabs(currColour.R() - nextColour.R()) >
COLOUR_CHANGE_SPEED) {
int changeSgn =
NumberFuncs::SignOf(nextColour.R() - currColour.R());
this->currColour[0] += changeSgn*colourChangeInc;
transitionDone = false;
}
if (fabs(currColour.G() - nextColour.G()) >
COLOUR_CHANGE_SPEED) {
int changeSgn =
NumberFuncs::SignOf(nextColour.G() - currColour.G());
this->currColour[1] += changeSgn*colourChangeInc;
transitionDone = false;
}
if (fabs(currColour.B() - nextColour.B()) >
COLOUR_CHANGE_SPEED) {
int changeSgn =
NumberFuncs::SignOf(nextColour.B() - currColour.B());
this->currColour[2] += changeSgn*colourChangeInc;
transitionDone = false;
}
// If we're close enough to the next colour
// then move on to the next colour
if (transitionDone) {
this->currColourIndex = (this->currColourIndex + 1) %
NUM_COLOUR_CHANGES;
}
cgGLSetParameter3f(this->colourMultParam,
this->currColour[0], this->currColour[1],
this->currColour[2]);