Assignment 8 – Building Shaders

The first part of this assignment was putting the “Effect” in a file, building the effect and loading the file at runtime. Its the same process as in Assignment 6 so I will not go over it in detail.

Here is the human-readable Effect file:

--[[
	This is a human readable effect file. It is structured as a Lua table.
]]

return
{
	vertex_shader = "data/shaders/square.vert",
	fragment_shader = "data/shaders/square.frag"
}

Which is compiled into a binary file:
effectbinary

The first byte is the length of the path to the vertex shader, followed by a null-terminated path. The next byte after that is the length of the path to the fragment shader, followed by a null-terminated path.

Following code extracts this data:

 
                // ...buffer contains the entire file...
                char * current = buffer;

                uint8_t pathLength = *reinterpret_cast(current);
                char * vert_shader_path = current + 1;
                
                current = current + 1 /*path length*/ + pathLength + 1 /* null terminator */;
                
                pathLength = *reinterpret_cast(current);
                char * frag_shader_path = current + 1;
                
                *o_effect = createEffect(vert_shader_path, frag_shader_path);

The next part of the assignment was to dedicated ShaderBuilder to build the shaders into a compiled format. The ShaderBuilder fits into the pipeline the same way as our other asset builders. Because we have two different types of shaders, we had the option of making two different builders (VertexShaderBuilder and FragmentShaderBuilder) or just use one ShaderBuilder and pass in a optional argument specifying which type of shader we are building. I decided to go with the latter approach because this means one less project in my solution which means faster compiles. Also, most of the code was already in place to take in optional arguments so it seemed like the easier approach.

In AssetsToBuild.lua, I have an additional optionalArguments table which are passed into the builder:

    vertex_shader = 
    {
        builder = "ShaderBuilder.exe",
		
		sourceExtension = ".vert",
        targetExtension = ".vert",

        dependencies = { "shaders\\shaders.inc" },
		optionalArguments = { "ShaderType_Vert" },
        assets = 
        {
            {src = "shaders\\square"}
        },
    },

We also have a special #define to control whether debug or release shaders are generated. This is there because we might just want to debug the shaders while have the rest of the game be compiled in release mode. It looks like this in code:

		commandToBuild << " /Emain"
			// #define the platform
			<< " /DEAE6320_PLATFORM_D3D"
#ifdef GRAPHICS_AREDEBUGSHADERSENABLED
			// Disable optimizations so that debugging is easier
			<< " /Od"
			// Enable debugging
			<< " /Zi"
#endif
			// Target file
			<< " /Fo\"" << m_path_target << "\""
			// Don't output the logo
			<< " /nologo"
			// Source file
			<< " \"" << m_path_source << "\""
		;

A comparison between the debug and release version of Direct3D vertex shaders:

debug_release_shadercomparision
Left: Debug version     Right: Release version

A comparison between debug and release version of OpenGL vertex shaders:

Left: Debug Right: Release
Left: Debug   Right: Release

The only difference between the two is that the debug version contains comments (and a whole bunch of whitespace), which might be useful for debugging.

The program output is the same as last time:

Assignment 7 screenshot

Controls:

Press Esc to quit. Arrow keys to move the square.

Download