Assignment 9 – Platform-independent Render and Shaders

This assignment was about completely abstracting the main render function and making semi-platform independent shaders.

I will just paste my function implementation below, it should be self-explanatory:

        void eae6320::Graphics::Render()
        {
            {
                GraphicsAPI::ClearScreen();
                
                {
                    GraphicsAPI::BeginScene();
                    // The actual function calls that draw geometry
                    for (auto it = s_objectsToRender.begin(); it != s_objectsToRender.end(); ++it)
                    {
                        BOOL result;
                        eae6320::Graphics::RenderObject* rObj = *it;
                        {
                            // Set the shaders
                            result = GraphicsAPI::SetEffect(rObj->m_effect);
                            assert(result);

                            float offset[] = { rObj->m_offset.x, rObj->m_offset.y };
                            result = GraphicsAPI::SetDrawCallUniforms(rObj->m_effect, offset);

                            result = GraphicsAPI::DrawMesh(rObj->m_mesh);
                            assert(result != FALSE);
                        }
                    }
                    GraphicsAPI::EndScene();
                }

                GraphicsAPI::DisplayRenderedBuffer();
            }
        }

The GraphicsAPI namespace indicates that the particular function has a platform-dependent implementation. Anything inside the Graphics namespace is completely platform agnostic.

The platform independent vertex shader looks like this (commets stripped):

/*
	This is an example of a vertex shader
*/

#include "shaders.inc"

uniform float2 g_position_offset;

#if defined( EAE6320_PLATFORM_D3D )

void main(
	in const float2 i_position : POSITION,
	in const float4 i_color : COLOR,
	out float4 o_position : POSITION,
	out float4 o_color : COLOR
	)

#elif defined( EAE6320_PLATFORM_GL )

layout( location = 0 ) in vec2 i_position;
layout( location = 1 ) in vec4 i_color;

layout( location = 0 ) out vec4 o_color;

void main()

#endif

{
	// Calculate the position of this vertex on screen
	{
		float4 out_pos = float4( i_position + g_position_offset, 0.0, 1.0 );
		
		// Set the outpout position - platform specific
		#if defined( EAE6320_PLATFORM_GL )
		gl_Position = out_pos;
		#elif defined ( EAE6320_PLATFORM_D3D )
		o_position = out_pos;
		#endif
	}

	// Pass the input color to the fragment shader unchanged:
	{
		o_color = i_color;
	}
}

There is a “shaders.inc” file included at the top. This file simply translates between different variable types and sets some defines for the respective API. This allows us to use variable types for either HLSL or GLSL and have it work for both platforms.

As far as the shader itself, there are differences in how the input and output variables are declared, but the shader logic has been made platform independent. There is an exception to setting the output variable however. OpenGL requires the use of a special variable called gl_position while Direct3D doesn’t. A simple #if defined is used here.

I am using HLSL variable type names for no real reason. I guess float4 sounds more descriptive than vec4. The shader is so simple I didn’t think about it. Probably will stick with HLSL.

Because the shaders now depend on shaders.inc, if there is any change in shaders.inc, we must ensure that the shaders are rebuilt. If we don’t do this, it might require us to manually delete the built shaders, so that they are rebuilt.

To do this, I added a dependencies table in my AssetsToBuild like so:

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

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

BuildAssets.lua then checks if there are any dependency for the asset type and triggers a rebuild if any of the dependency changed.


The final result still looks the same as last time:

Assignment 7 screenshot

Controls:

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

Download