ShaderGen
Proof-of-concept library for generating HLSL, GLSL, and Metal shader code from C#,
Install / Use
/learn @mellinoe/ShaderGenREADME
ShaderGen
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_;
}
Related Skills
node-connect
335.9kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
82.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
335.9kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
82.7kCommit, push, and open a PR
