This blog series is a part of the write-up assignments of our Game Engineering II class in the Master of Entertainment Arts & Engineering program at University of Utah.
In this assignment, we are making a human-readable effect format, create an effect builder project to build the binary file from that format, and then read it in. This assignment is basically a combination of the previous three or four assignments together.
There are three elements that we need for our effect to work, render state, vertex shader, and fragment shader. We know that the render state is represented with a uint8_t in our graphics system since we wrote the engine, but the user does not know this. I chose to provide these variables to allow the different effects to specify whether enable it (true) or not (false).
Binary File Extraction
Our successfully build binary file with look like this. The first byte is the render state, which in this case is 2. The second byte 24 in hex means 36 in decimal which is the length of the path to the vertex shader. You can see right here, the paths start with data/, which were appended during build-time. I decided that the 5 bytes memory usage cannot compare to run-time performance, this may change in the cases of different games though.
Extracting the effect data from binary files is relatively easy. This is basically the same as how I loaded the binary mesh files. However, since we are handling paths as strings, we need to be extra careful with the null terminator \0. When we exported the vertex shader path length, we actually needed to increment by 1 to include \0.
This assignment doesn’t change anything visually with our game, so I put in more animals, and apparently, a rabbit should be bigger than a deer or a wolf, so this is what it looks like, kinda looks like a music album cover.
More Platform Independent Shader Files
Since we’re supporting both D3D and OpenGL, Our shader files contain both GLSL and HLSL. The appropriate parts get built to the corresponding folders during build time to generate the final shaders that are actually used. It would make sense that we want to minimize duplicated codes.
Since we have a shader.inc that is included by all shaders, we can define some macros inside it and declare some variable types to get rid of some duplicate codes. Such as defining all GLSL variables to HLSL variable,
#if defined( EAE6320_PLATFORM_D3D ) #define DeclareConstantBuffer( i_name, i_id ) cbuffer i_name : register( b##i_id ) #define Transform(i_matrix, i_vector) mul(i_matrix, i_vector) #elif defined( EAE6320_PLATFORM_GL ) #define float4x4 mat4 #define DeclareConstantBuffer( i_name, i_id ) layout( std140, binding = i_id ) uniform i_name #define Transform(i_matrix, i_vector) (i_matrix*i_vector) #endif
It would seem like we can easily change all the files with this principle. However, OpenGL requires a gl_Position output from the vertex shader. Also, there is multiple inputs and output variables syntax that we need to handle. It will take more than what we have now to make our shaders more platform independent.
Hold down “Z” to change the wolf object into a bunny. Use “WASD” to move the camera, “J” and “L” to rotate the camera horizontally. “Arrow keys” to move the wolf object. “E” and “R” to rotate the deer.