Introduction
First of all, Why learn 3D projections in the GPU? Well, mainly because the GPU is epic, and secondly because this has LOTS of potential. No other screen in wiremod has 3D functionality and best of all, its purely client-side so the stuff you make will have ultra-fast update rates. (Based on your frames per second).
Important Note - (May 2011)
I made this tutorial for the older GPU which only supported ZASM code at the time.
If you would like to write the code in C, visit the following thread: Graphics Library Documentation
Examples
First lets have a look at some of the inspirational stuff people have made using 3D features in the GPU:
Minecraft (by VonCastren)
Guitar Hero (by Matte)
Garrysmod Simulation (by Matte)
Gravity-Ball 3D (by Drunkie)
3D Projections Template
I have a template you should use for every 3D projection you make, it will help you out by having it saved instead of re-typing it all the time. Don't worry if this looks intimidating at first, I will explain everything. I strongly recommend using this template. After using this quite a few times you will become familiar with how it works as well as the necessary steps required in 3D projections.
Code:
// Initialize
dclrscr bg_color;
dcpipe 3; // -1..1 coordinates
dvxpipe 5; // Matrix projection
// Matrix functions
mlookat mViewMatrix,vLookAt; // Look At
mperspective mProjectionMatrix,vPerspective; // Perspective
mrotate mRotateMatrix,vRotate; // Rotate
mtranslate mTranslateMatrix,vTranslate; // Translate
mmov mModelMatrix,mTranslateMatrix; // Create model matrix
mmul mModelMatrix,mRotateMatrix;
mmov mModelViewMatrix,mViewMatrix; // Create model view matrix
mmul mModelViewMatrix,mModelMatrix;
mload mModelViewMatrix; // Load matrix
mloadproj mProjectionMatrix;
// Rotate model
timer #vRotate.w;
// Enable matrix features
dsetlight 0,LightData; //Setup light
denable 0; //Vertex buffer
denable 1; //ZSortings
denable 2; //Lighting
denable 3; //Face culling
// Render
dvxdata_3f VertexData,1; // Load vertex data
dvxflush; // Flush buffer to screen
dexit;
// Variables & Labels
color bg_color,0,0,0;
matrix mRotateMatrix;
matrix mTranslateMatrix;
matrix mProjectionMatrix; // This defines our projection to screen
matrix mViewMatrix; // This defines our camera transformations
matrix mModelMatrix; // This is our model transformations
matrix mModelViewMatrix; // This is our model relative to camera transform
vLookAt:
vec3f vLookAt_Eye, 0, 0, -5; // Where our camera is
vec3f vLookAt_Center, 0, 0, 0; // What we look at
vec3f vLookAt_Up, 0, 1, 0; // Where our matt-hat is
vec4f vRotate, 0, 1, 0, 1; // <AXIS X Y Z> <ANGLE W>
vec4f vTranslate, 0, 0, 0, 0; // <TRANSLATION X Y Z> <0>
vec4f vPerspective, 30, 1, 1, 20; // <FOV> <ASPECT RATIO> <ZNEAR> <ZFAR>
LightData:
vec4f lightpos,0,0,-50,0; // x y z <unused, will be falloff>
color lightcol,255,255,255,1; // R G B Brightness
VertexData:
db 0,0,0; db 0,0,0; db 0,0,0;
This template is pretty nice to use, because all you need to do is edit the variables, and give it your own set of vertices to make the object you want to create. The code as is, will not render anything because it has no vertices defined.
Step-by-Step Explanation
This will clear the background color to whatever the bg_color variable is set to.
This will set the coordinates on the screen to -1 to 1 instead of the native 0 to 512.
This puts us in matrix projection mode.
Code:
mlookat mViewMatrix,vLookAt; // Look At
mperspective mProjectionMatrix,vPerspective; // Perspective
mrotate mRotateMatrix,vRotate; // Rotate
mtranslate mTranslateMatrix,vTranslate; // Translate
This is the part where we do our matrix opcodes. If you don't understand this fully, don't worry, you don't really need to right away. Once you make a few programs, you will understand these a lot better.
The mlookat opcode will set our view matrix based on vLookAt which is a label.
The mperspective opcode will set our projection matrix based on the variable vPerspective which is a vector.
The mrotate opcode will set our rotate matrix based on the variable vRotate which is a vector.
The mtranslate opcode will set our translation matrix based on the variable vTranslate which is a vector.
Code:
mmov mModelMatrix,mTranslateMatrix;
mmul mModelMatrix,mRotateMatrix;
Here, we basically create the model matrix by doing:
ModelMatrix = TranslateMatrix
ModelMatrix = ModelMatrix * RotateMatrix
Code:
mmov mModelViewMatrix,mViewMatrix;
mmul mModelViewMatrix,mModelMatrix;
Now we create a model view matrix by doing:
ModelViewMatrix = ViewMatrix
ModelViewMatrix = ModelViewMatrix * ModelMatrix
Now we are done with the matrix math.
Code:
mload mModelViewMatrix;
mloadproj mProjectionMatrix;
This will load the ModelViewMatrix, and the ProjectionMatrix.
Now, optionally, we can choose if we want our model to rotate on the screen. We basically set the w value of vRotate (A vector) to a incrementing timer. When we get to the variables part, you will see that W is the angle.
Code:
dsetlight 0,LightData;
Now we need a light source. This will set our light source to the LightData label.
Code:
denable 0; //Vertex buffer
denable 1; //ZSortings
denable 2; //Lighting
denable 3; //Face culling
Now we enable all of the matrix features here.
0 - Enable vertex buffer
1 - Enable ZSorting in vertex buffer (sorted on flush)
2 - Enable vertex lighting
3 - Enable culling on faces
Code:
dvxdata_3f VertexData,1;
This is an important part of the program. This opcode looks for a pointer in memory to where all of your vertices are stored (In this case, we provide the label VertexData). The second parameter is for how many triangles to render (Below in the VertexData label, for every 3 db definitions we have 1 triangle) . If you didn't know by now, everything in 3D is rendered with triangles, therefore, you have to construct shapes using a series of triangles.
Now we use the opcode dvxflush to "flush" the vertex buffer to the screen.
I'm sure you know what this means... This marks the end of the program where it will exit and stop rendering. Now we have to make all the variables and labels we used under dexit.
Code:
color bg_color,0,0,0;
Pretty self-explanatory. This is our background color; you can change the RGB values to whatever you like.
Code:
matrix mRotateMatrix;
matrix mTranslateMatrix;
matrix mProjectionMatrix;
matrix mViewMatrix;
matrix mModelMatrix;
matrix mModelViewMatrix;
Now we make all of our matrix variables we used.
Code:
vLookAt:
vec3f vLookAt_Eye, 0, 0, -5; // Where our camera is
vec3f vLookAt_Center, 0, 0, 0; // What we look at
vec3f vLookAt_Up, 0, 1, 0; // Where our matt-hat is
Here is the vLookAt label. In this, we have 3 vectors. The only one you should touch is the vLookAt_Eye vector, because this is the direction of the camera. Assuming that you leave vLookAt_Center looking at 0,0,0 then vLookAt_Eye is the only thing you should edit.
Code:
vec4f vRotate, 0, 1, 0, 1; // <AXIS X Y Z> <ANGLE W>
This vector will set the rotation of the model. Axis X, Y and Z are equivalent to Pitch, Yaw, Roll. Editing this will affect the entire rotation of ALL vertices in the projection.
Code:
vec4f vTranslate, 0, 0, 0, 0; // <TRANSLATION X Y Z> <0>
This vector will basically move the model around the screen. X, Y and Z will dictate where the object appears on the screen (To the left, right, up or down, etc). By default, having this as 0,0,0 will make it appear in the center of the screen.
Code:
vec4f vPerspective, 30, 1, 1, 20; // <FOV> <ASPECT RATIO> <ZNEAR> <ZFAR>
This is a very important aspect to the projection. This dictates the perspective that (you the user) will see.
The X value, FOV (Field of View) is how much you see in your view. If you think about your FOV in Gmod, it's how much you see in your vision. If your FOV is high, you will see alot more, if your FOV is small, you will have a narrower vision.
The Y value, Aspect Ratio is how the object is stretched. When aspect ratio is greater than 1, it stretches the height of the object. When the aspect ratio is less than 1, it stretches the width of the object.
The Z and W values ZNEAR and ZFAR are used to define where the objects are clipped. (Need more info on this from Black Phoenix)
Code:
LightData:
vec4f lightpos,0,0,-50,0; // x y z <unused, will be falloff>
color lightcol,255,255,255,1; // R G B Brightness
Next, we make our LightData label. Basically this is responsible for the shading/lighting of the object we are projecting. A label that contains light data will contain a vector4. This vector will define what position our light source is coming from. We have X, Y and Z. The last value is unused.
The next piece in the label is the color of the light. Here we will simply give a R, G, B and Brightness value. The last value will obviously determine how bright our light is shining on the object. Default brightness is 1. Anything higher will increase brightness, but it also takes into account the position of your light source. If your light's position is far away you will need a higher brightness, and when the light is very close, you need a smaller brightness.
Code:
VertexData:
db 0,0,0; db 0,0,0; db 0,0,0;
Now we have one last thing to do. In this label VertexData, we need to define each vertex to construct a series of triangles that ultimately builds an object to project. When we define a vertex we need to give it X, Y and Z. To make one triangle we need 3 db definitions. If we define vertices that connect in a clockwise order, the polygon will be drawn to face the camera. If the vertices connect in a counter-clockwise order, the polygon will be drawn away from us.
This picture may help you in figuring out vertices in 3D space (Taken from the other GPU tutorial).

Also, this picture may help you in understanding the coordinates in 3D (Thanks to Danking).

Video Explanation
Here I'll go through the steps in defining each vertex for a pyramid object, setting up lighting, color, rotation, translation and perspective.
Object Examples
Here some objects I have made from scratch that you may use. Remember to change the line dvxdata_3f VertexData,1 to reflect how many triangle polys are being rendered.
Icosahedron (20 polys):
Code:
db 0,0,1; db 0,0.9,0.5; db 0.9,0.3,0.4;
db 0,0,1; db -0.9,0.3,0.4; db 0,0.9,0.5;
db 0,0,1; db -0.5,-0.7,0.4; db -0.9,0.3,0.4;
db 0,0,1; db 0.5,-0.7,0.4; db -0.5,-0.7,0.4;
db 0,0,1; db 0.9,0.3,0.4; db 0.5,-0.7,0.4;
db 0.9,-0.3,-0.4; db 0.9,0.3,0.4; db 0.5,0.7,-0.4;
db 0,0.9,0.5; db 0.5,0.7,-0.4; db 0.9,0.3,0.4;
db 0,0.9,0.5; db -0.5,0.7,-0.4; db 0.5,0.7,-0.4;
db 0,0.9,0.5; db -0.9,0.3,0.4; db -0.5,0.7,-0.4;
db -0.9,-0.3,-0.4; db -0.5,0.7,-0.4; db -0.9,0.3,0.4;
db -0.9,-0.3,-0.4; db -0.9,0.3,0.4; db -0.5,-0.7,0.4;
db -0.9,-0.3,-0.4; db -0.5,-0.7,0.4; db 0,-0.9,-0.5;
db 0.5,-0.7,0.4; db 0,-0.9,-0.5; db -0.5,-0.7,0.4;
db 0.5,-0.7,0.4; db 0.9,-0.3,-0.4; db 0,-0.9,-0.5;
db 0.5,-0.7,0.4; db 0.9,0.3,0.4; db 0.9,-0.3,-0.4;
db 0,0,-1; db 0,-0.9,-0.5; db 0.9,-0.3,-0.4;
db 0,0,-1; db 0.9,-0.3,-0.4; db 0.5,0.7,-0.4;
db 0,0,-1; db 0.5,0.7,-0.4 db -0.5,0.7,-0.4;
db 0,0,-1; db -0.5,0.7,-0.4; db -0.9,-0.3,-0.4;
db 0,0,-1; db -0.9,-0.3,-0.4; db 0,-0.9,-0.5;
Letter A (30 polys):
Code:
db 1,1,0; db 0.75,1,0; db 0.25,-1,0; // Left Slope Front
db 0.75,1,0; db 0,-1,0; db 0.25,-1,0;
db -1,1,0; db -0.25,-1,0; db -0.75,1,0; // Right Slope Front
db -0.75,1,0; db -0.25,-1,0; db 0,-1,0;
db 1,1,0.25; db 0.25,-1,0.25; db 0.75,1,0.25; // Left Slope Behind
db 0.75,1,0.25; db 0.25,-1,0.25; db 0,-1,0.25;
db -1,1,0.25; db -0.75,1,0.25; db -0.25,-1,0.25; // Right Slope Behind
db -0.75,1,0.25; db 0,-1,0.25; db -0.25,-1,0.25;
db 0.25,-1,0; db -0.25,-1,0; db 0.25,-1,0.25; // Top Portion
db -0.25,-1,0; db -0.25,-1,0.25; db 0.25,-1,0.25;
db -1,1,0; db -1,1,0.25; db -0.25,-1,0; // Left Side
db -1,1,0.25; db -0.25,-1,0.25; db -0.25,-1,0;
db 1,1,0.25; db 1,1,0; db 0.25,-1,0; // Right Side
db 1,1,0.25; db 0.25,-1,0; db 0.25,-1,0.25;
db -0.75,1,0; db 0,-1,0; db -0.75,1,0.25; // Inner Left
db -0.75,1,0.25; db 0,-1,0; db 0,-1,0.25;
db 0.75,1,0; db 0.75,1,0.25; db 0,-1,0; // Inner Right
db 0.75,1,0.25; db 0,-1,0.25; db 0,-1,0;
db -0.47,0.25,0; db -0.38,0.01,0; db 0.38,0.01,0; // Middle Stem Front
db 0.38,0.01,0; db 0.47,0.25,0; db -0.47,0.25,0;
db -0.47,0.25,0.25; db 0.38,0.01,0.25; db -0.38,0.01,0.25; // Middle Stem Behind
db 0.38,0.01,0.25; db -0.47,0.25,0.25; db 0.47,0.25,0.25;
db -0.38,0.01,0; db -0.38,0.01,0.25; db 0.38,0.01,0; // Middle Stem Upper
db 0.38,0.01,0; db -0.38,0.01,0.25; db 0.38,0.01,0.25;
db -0.47,0.25,0; db 0.47,0.25,0; db -0.47,0.25,0.25; // Middle Stem Lower
db -0.47,0.25,0.25; db 0.47,0.25,0; db 0.47,0.25,0.25;
db -1,1,0; db -0.75,1,0; db -1,1,0.25; // Left Foot
db -0.75,1,0; db -0.75,1,0.25; db -1,1,0.25;
db 1,1,0; db 1,1,0.25; db 0.75,1,0; // Right Foot
db 0.75,1,0; db 1,1,0.25; db 0.75,1,0.25;
Maple Leaf (21 polys):
This is one sided, disable face culling to render both sides (Comment out the line "denable 3")
Code:
db -0.633,-0.172,0; db -0.004,-0.172,0; db -0.551,0.117,0;
db -0.004,-0.172,0; db -0.25,0.383,0; db -0.551,0.117,0;
db -0.004,-0.172,0; db 0.246,0.383,0; db -0.25,0.383,0;
db -0.004,-0.172,0; db 0.547,0.117,0; db 0.246,0.383,0;
db -0.004,-0.172,0; db 0.633,-0.172,0; db 0.547,0.117,0;
db -0.145,-0.52,0; db -0.004,-0.789,0; db -0.004,-0.453,0;
db -0.004,-0.453,0; db -0.004,-0.789,0; db 0.145,-0.52,0;
db -0.27,-0.594,0; db -0.004,-0.453,0; db -0.004,-0.172,0; // 8
db -0.27,-0.594,0; db -0.004,-0.172,0; db -0.211,-0.172,0;
db 0.266,-0.594,0; db -0.004,-0.172,0; db -0.004,-0.453,0;
db 0.266,-0.594,0; db 0.211,-0.172,0; db -0.004,-0.172,0;
db -0.023,0.773,0; db -0.016,0.383,0; db 0.023,0.773,0;
db 0.023,0.773,0; db -0.016,0.383,0; db 0.012,0.383,0;
db -0.371,0.43,0; db -0.344,0.305,0; db -0.043,0.383,0;
db 0.371,0.43,0; db 0.043,0.383,0; db 0.34,0.305,0;
db -0.551,0.117,0; db -0.699,-0.004,0; db -0.598,-0.051,0; // 16
db 0.547,0.117,0; db 0.594,-0.051,0; db 0.699,-0.004,0;
db -0.633,-0.172,0; db -0.672,-0.305,0; db -0.25,-0.172,0;
db 0.633,-0.172,0; db 0.246,-0.172,0; db 0.668,-0.305,0;
db -0.473,-0.219,0; db -0.418,-0.367,0; db -0.25,-0.172,0;
db 0.473,-0.219,0; db 0.246,-0.172,0; db 0.418,-0.367,0;
Bookmarks