Skip to content
Proof-of-concept library for generating HLSL, GLSL, and Metal shader code from C#,
C#
Branch: master
Clone or download
Latest commit badb819 Jan 30, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
src Remove some unnecessary project properties. Jan 30, 2019
.editorconfig Swizzle improvements. Sep 22, 2017
.gitignore Added JetBrains Rider's .idea directory to gitignore Jan 30, 2018
Directory.Build.props Update copyright date. Jan 30, 2019
LICENSE Add MIT license notice. Jan 28, 2018
NuGet.Config Implemented Math functions, automated headless graphics device testin… May 30, 2018
README.md Update README.md Apr 18, 2018
appveyor.yml Run tests in CI. Mar 29, 2018
build-packages.cmd Add ShaderGen.csproj to the list of packages being produced. Sep 22, 2017
build-packages.sh
ci-build.cmd Add auto-versioning and AppVeyor stuff. Nov 18, 2017
ci-test.cmd Run tests in CI. Mar 29, 2018
version.json Change version to 1.2.0-beta3. Jan 30, 2019

README.md

ShaderGen

Gitter Chat

A proof-of-concept library which generates shader code from C#. Currently, the project can generate HLSL (D3D11), GLSL-330 (core-GL-compatible), GLSLES-300 (OpenGL ES-compatible), GLSL-450 (Vulkan-compatible), and Metal shader code from a single shader source specified in C#.

Shaders in C#

Writing shader code in C# could have quite a few benefits:

  • Easily share type definitions between graphics code in C# and shaders.
    • For example, one could re-use the same structure to describe the input to a vertex shader, as well as to store the actual vertex data in your C# program.
    • Shader uniforms ("constant buffers") can be shared as well.
  • Analysis done at code-generation time can be used to build extra metadata about the shaders, enabling reflection-like capabilities.
    • The full vertex input specification can be generated when doing the C# analysis for code generation.
    • The layouts and order for all global shader resources can be captured.
    • Validation can be performed to ensure, for example, uniforms are multiples of 16-bytes in size, etc.
  • C# refactoring tools can be used.
  • C# niceties like inheritance, composition, partial declarations, etc. can be leveraged for easier shader writing (speculative).

Example Shader

Here is an example vertex and fragment shader, written in C# with ShaderGen:

public class MinExample
{
    public Matrix4x4 Projection;
    public Matrix4x4 View;
    public Matrix4x4 World;
    public Texture2DResource SurfaceTexture;
    public SamplerResource Sampler;

    public struct VertexInput
    {
        [PositionSemantic] public Vector3 Position;
        [TextureCoordinateSemantic] public Vector2 TextureCoord;
    }

    public struct FragmentInput
    {
        [SystemPositionSemanticAttribute] public Vector4 Position;
        [TextureCoordinateSemantic] public Vector2 TextureCoord;
    }

    [VertexShader]
    public FragmentInput VertexShaderFunc(VertexInput input)
    {
        FragmentInput output;
        Vector4 worldPosition = Mul(World, new Vector4(input.Position, 1));
        Vector4 viewPosition = Mul(View, worldPosition);
        output.Position = Mul(Projection, viewPosition);
        output.TextureCoord = input.TextureCoord;
        return output;
    }

    [FragmentShader]
    public Vector4 FragmentShaderFunc(FragmentInput input)
    {
        return Sample(SurfaceTexture, Sampler, input.TextureCoord);
    }
}

Here is some representative output from the library (subject to change, etc.):

HLSL Vertex Shader

struct MinExample_VertexInput
{
    float3 Position : POSITION0;
    float2 TextureCoord : TEXCOORD0;
};

struct MinExample_FragmentInput
{
    float4 Position : SV_Position;
    float2 TextureCoord : TEXCOORD0;
};

cbuffer ProjectionBuffer : register(b0)
{
    float4x4 Projection;
}

cbuffer ViewBuffer : register(b1)
{
    float4x4 View;
}

cbuffer WorldBuffer : register(b2)
{
    float4x4 World;
}

MinExample_FragmentInput VertexShaderFunc( MinExample_VertexInput input)
{
    MinExample_FragmentInput output;
    float4 worldPosition = mul(World, float4(input.Position, 1));
    float4 viewPosition = mul(View, worldPosition);
    output.Position = mul(Projection, viewPosition);
    output.TextureCoord = input.TextureCoord;
    return output;
}

HLSL Fragment Shader

struct MinExample_VertexInput
{
    float3 Position : POSITION0;
    float2 TextureCoord : TEXCOORD0;
};

struct MinExample_FragmentInput
{
    float4 Position : SV_Position;
    float2 TextureCoord : TEXCOORD0;
};

Texture2D SurfaceTexture : register(t0);

SamplerState Sampler : register(s0);

float4 FragmentShaderFunc( MinExample_FragmentInput input) : SV_Target
{
    return SurfaceTexture.Sample(Sampler, input.TextureCoord);
}

GLSL (450) Vertex Shader

#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
struct MinExample_VertexInput
{
    vec3 Position;
    vec2 TextureCoord;
};

struct MinExample_FragmentInput
{
    vec4 Position;
    vec2 TextureCoord;
};

layout(set = 0, binding = 0) uniform Projection
{
    mat4 field_Projection;
};

layout(set = 0, binding = 1) uniform View
{
    mat4 field_View;
};

layout(set = 0, binding = 2) uniform World
{
    mat4 field_World;
};

layout(set = 0, binding = 3) uniform texture2D SurfaceTexture;
layout(set = 0, binding = 4) uniform sampler Sampler;
MinExample_FragmentInput VertexShaderFunc( MinExample_VertexInput input_)
{
    MinExample_FragmentInput output_;
    vec4 worldPosition = field_World * vec4(input_.Position, 1);
    vec4 viewPosition = field_View * worldPosition;
    output_.Position = field_Projection * viewPosition;
    output_.TextureCoord = input_.TextureCoord;
    return output_;
}


layout(location = 0) in vec3 Position;
layout(location = 1) in vec2 TextureCoord;
layout(location = 0) out vec2 fsin_0;

void main()
{
    MinExample_VertexInput input_;
    input_.Position = Position;
    input_.TextureCoord = TextureCoord;
    MinExample_FragmentInput output_ = VertexShaderFunc(input_);
    fsin_0 = output_.TextureCoord;
    gl_Position = output_.Position;
        gl_Position.y = -gl_Position.y; // Correct for Vulkan clip coordinates
}

GLSL (450) Fragment Shader

#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_ARB_shading_language_420pack : enable
struct MinExample_VertexInput
{
    vec3 Position;
    vec2 TextureCoord;
};

struct MinExample_FragmentInput
{
    vec4 Position;
    vec2 TextureCoord;
};

layout(set = 0, binding = 0) uniform Projection
{
    mat4 field_Projection;
};

layout(set = 0, binding = 1) uniform View
{
    mat4 field_View;
};

layout(set = 0, binding = 2) uniform World
{
    mat4 field_World;
};

layout(set = 0, binding = 3) uniform texture2D SurfaceTexture;
layout(set = 0, binding = 4) uniform sampler Sampler;
vec4 FragmentShaderFunc( MinExample_FragmentInput input_)
{
    return texture(sampler2D(SurfaceTexture, Sampler), input_.TextureCoord);
}


layout(location = 0) in vec2 fsin_0;
layout(location = 0) out vec4 _outputColor_;

void main()
{
    MinExample_FragmentInput input_;
    input_.Position = gl_FragCoord;
    input_.TextureCoord = fsin_0;
    vec4 output_ = FragmentShaderFunc(input_);
    _outputColor_ = output_;
}
You can’t perform that action at this time.