Sunday, October 23, 2011

Projective Texturing

Sometimes we will need to apply a texture just like a projector would. This technique can help us create different effects like the Batman sign on the night sky or water caustics.


To begin this tutorial please download the starting source code. The project already has code to draw the crate and the second texture loaded. What we will have to do is simply add the projective texturing calculations to it.
To add projective texturing, we will need a matrix that will be able to calculate the coordinates of a object in respect to a source, if you remember from the simple drawing tutorial, we did such a calculation for the camera. Projective texturing uses the same type of calculations to get it’s result. Imagine that the source of the texture is a projector (light) and the camera eye (from the view matrixes) is the lens of the projector. Of course the world transformation is the same, what will differ will be the view matrix from the projector to the direction it is pointed and the projection matrix, that defines the size and shape of the light volume. If you remember, these matrixes generate coordinates in the [–1, 1] range. Textures require coordinates in the [0,1] range, so it will be necessary to scale and translate the coordinates in such a way that they will make sense as texture coordinates, because of this we will have an extra correction matrix. Go to the top of your game class and add these 3 matrixes:

        Matrix pLookAt;
Matrix pCorection;

Now that we have the matrixes we need to create them. Go in the LoadContent function and add these 3 lines. They will set the projector to be a little to the right of the camera and oriented towards the object and give it a big enough volume to cover most of it. The correction matrix, as you can see simply changes our domain from [-1,1] to [0,1] in each of the 3 dimensions.

    pLookAt = Matrix.CreateLookAt(new Vector3(3, 0, 5), Vector3.Zero, Vector3.Down);
    pProj =
.ToRadians(15), 1, 1, 10);
    pCorection =
Matrix.CreateScale(0.5f, 0.5f, 0.5f) * Matrix.CreateTranslation(0.5f, 0.5f, 0.5f);

After this, all we have to do in our game class is to set the matrix for the projection. You should do this step in the GameDraw function just before you render the cube.

    projectedTextureEffect.Parameters["lightWVP"].SetValue(World * pLookAt * pProj * pCorection);

Now that we are done with our C# code, we should focus a little on the shader. The texture and the sampler are already added. We need to calculate the projected texture coordinates and then send them to the pixel shader so it can sample the texture. For this we will need to add a float4 (the texture coordinates spatial) with the semantic TEXCOOD1 to our  VertexShaderOutput structure.

    float4 ProjPoz  : TEXCOORD1;

As I said before, we do the shading in 2 parts, first we need to transform the object vertices by the matrix we have set from the game class:

    output.ProjPoz = mul(input.Position, lightWVP);

And in the pixel shader we have to somehow combine the color of the textures, I decided to do a simple addition as the smiley texture I used has a very strong yellow so it will completely convert the color of the texture:

    return tex2D(texSampler,input.Texture) + tex2Dproj(texSamplerP,input.ProjPoz);

That should do it, now you should have a project with projective texturing (link to final project), and the running application should look like so:

No comments:

Post a Comment