This recipe is going to create an application that loads a texture from an image file and displays it centered on the screen.
This example is actually rather daunting, throwing quite a bit of information at a new developer. Don't worry if it seems overly complex for now; by the end of the book it will make more sense. If you feel overwhelmed, I recommend you continue the book and revisit this recipe later.
Phew! That sure seemed like a lot of code to simply display a single image on screen, didn't it? The truth is, you did a lot more than just load and draw a texture. Let's jump in and look at exactly what we just created.
First, we declared the following variables, in addition to our existing GraphicsContext
variable:
_texture
is our Texture2D
object that is going to hold our textured image.
_vertexBuffer
is a VertexBuffer
object that holds the 3D quad geometry we are going to map our texture on.
_shaderProgram
is a ShaderProgram
variable, the texture shader needed to render our texture. The GraphicsContext
variable requires at least one. Fortunately, a simple one with the extension .cgx
was created for you already by PSM Studio when you created the project.
_localMatrix
, _projectionMatrix
, and _viewMatrix
are Matrix4
objects, representing the textured object's position.
_viewportWidth
and _viewportHeight
contain the dimensions of our window.
The bulk of our activity is in the Initialize()
method. Once again, we create a GraphicsContext
variable, and then store the dimensions of the frame buffer in the _viewportHeight
and _viewportWidth
variables. Next, we create our Texture2D
object, passing the constructor the filename and whether or not we want a mipmap generated.
Next, we create a _vertexBuffer
object, which is going to be a fullscreen quad we can draw our texture on. We make two calls to SetVertices()
. The first call is defining the x, y, and z float variables that make up the four vertices of the fullscreen quad. The second SetVertices
function call is four x and y texture coordinates. Texture coordinates are represented with a value from 0 to 1.
Next, we create our _textureShaderProgram
function using the default shader PSM Studio created for us. We will cover shaders in more detail later in this chapter.
Finally, we set up the _projectionMatrix
, _viewMatrix
, and _localMatrix
objects. The projection matrix is an orthographical matrix that represents our screen. The view matrix represents the camera within the world, using Matrix4.LookAt
. LookAt()
, which requires 3 vectors, the first representing your eye's location in 3D space, the second, the 3D point you are looking at, and the third, the direction where "UP" is, in this case in the Y direction. Finally, the local matrix represents the position of texture, which we want to be centered in the middle of the screen.
Now, let's take a look at the Render()
function, where our texture is going to be displayed to the screen. As before, we set the clear color to black and clear the buffer. Next, we generate our worldViewProjection
matrix by multiplying our projection, view and local matrices together. We then bind our worldViewProjection
matrix to our shader
program and then set our shader
program to the GraphicsContext
variable. We also set our VertexBuffer
object and Texture2D
object to the GraphicsContext
variable. The DrawArrays()
call is what ties it all together, using our worldViewMatrix
to transform our vertices from our VertexBuffer
object and applying our texture map, rendering it all to the active buffer. Finally, we make that buffer visible, which draws it on screen.
Here is our program in action, rendering our sprite centered to the screen:
Again, if that seemed overly complex, don't panic! Most of this code only needs to be written once, and you have the option of not working at this low a level if you should choose!
Build actions will be executed when your project is compiled, copying the content to the appropriate folder, performing whatever conversions are required. If you are used to XNA, this is similar to the functionality
of the content pipeline, but not programmable.
Note
Why is there 3D in my 2D?
The bulk of this example was actually going through the process of faking a 2D environment using 3D. The reason is modern GPUs are optimized to work in 3D. If you look at the code to most modern 2D libraries, they are actually working in 3D. If you were to work with native 2D graphics libraries, your performance would be abysmal.
An explanation of 3D mathematics is beyond the scope of this book, but the Kahn Academy (see http://www.khanacademy.org/) is an excellent free resource with thousands of video tutorials.
Tip
The sprite I used for this example and throughout this book is from a wonderful free sprite library made available by GameDev.net user Prince Eugn. You can find more information and download the sprite pack at http://bit.ly/N7CPtE.