This commit is contained in:
2024-12-04 22:35:04 +03:00
commit 3f1740f41f
43 changed files with 1757 additions and 0 deletions

View File

@@ -0,0 +1,181 @@
using System.Runtime.CompilerServices;
using System.Text;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;
using Serilog;
namespace Engine.Renderer.Shader;
public class ShaderProgram : OpenGlObject
{
private readonly Dictionary<string, int> _uniforms = new();
public static ShaderProgram CreateFromSource(string source)
{
var vertexSource = new StringBuilder();
var fragmentSource = new StringBuilder();
var inFragment = false;
var inVertex = false;
foreach (var line in source.Split('\n'))
{
if (line.StartsWith("#shader vertex"))
{
inVertex = true;
inFragment = false;
}
else if (line.StartsWith("#shader fragment"))
{
inVertex = false;
inFragment = true;
}
else if (inVertex)
{
vertexSource.AppendLine(line);
}
else if (inFragment)
{
fragmentSource.AppendLine(line);
}
}
return new ShaderProgram(vertexSource.ToString(), fragmentSource.ToString());
}
public ShaderProgram(string vertexSource, string fragmentSource)
{
var vertexShader = CompileSource(vertexSource, ShaderType.VertexShader);
var fragmentShader = CompileSource(fragmentSource, ShaderType.FragmentShader);
Handle = LinkProgram(vertexShader, fragmentShader);
}
public void SetUniform<T>(string name, T value, [CallerMemberName] string caller = "")
{
try
{
var location = GetUniformLocation(name);
switch (value)
{
case bool boolValue:
GL.ProgramUniform1(Handle, location, boolValue ? 1 : 0);
break;
case int intValue:
GL.ProgramUniform1(Handle, location, intValue);
break;
case float floatValue:
GL.ProgramUniform1(Handle, location, floatValue);
break;
case Vector2 vec2:
GL.ProgramUniform2(Handle, location, vec2);
break;
case Vector3 vec3:
GL.ProgramUniform3(Handle, location, vec3);
break;
case Vector4 vec4:
GL.ProgramUniform4(Handle, location, vec4);
break;
case Matrix4 matrix:
GL.ProgramUniformMatrix4(Handle, location, false, ref matrix);
break;
default:
throw new ArgumentException($"Unsupported uniform type: {typeof(T).Name}");
}
}
catch (Exception ex)
{
Log.Error(ex, "Failed to set uniform {UniformName} from {Caller}", name, caller);
throw;
}
}
internal override void Bind()
{
GL.UseProgram(Handle);
}
internal override void Unbind()
{
GL.UseProgram(0);
}
protected override void Destroy()
{
GL.DeleteProgram(Handle);
}
private int GetUniformLocation(string name)
{
if (_uniforms.TryGetValue(name, out var location))
return location;
location = GL.GetUniformLocation(Handle, name);
if (location < 0)
throw new ArgumentException($"Uniform '{name}' not found in shader program");
_uniforms.Add(name, location);
return location;
}
private static int CompileSource(string source, ShaderType type)
{
var shaderId = GL.CreateShader(type);
GL.ShaderSource(shaderId, source);
GL.CompileShader(shaderId);
GL.GetShader(shaderId, ShaderParameter.CompileStatus, out var status);
if (status == 0)
{
var log = GL.GetShaderInfoLog(shaderId);
GL.DeleteShader(shaderId);
throw new ShaderCompilationException(type, log);
}
return shaderId;
}
private static int LinkProgram(int vertexShader, int fragmentShader)
{
var programId = GL.CreateProgram();
try
{
GL.AttachShader(programId, vertexShader);
GL.AttachShader(programId, fragmentShader);
GL.LinkProgram(programId);
GL.GetProgram(programId, GetProgramParameterName.LinkStatus, out var linkStatus);
if (linkStatus == 0)
{
var log = GL.GetProgramInfoLog(programId);
throw new ShaderLinkException(log);
}
GL.ValidateProgram(programId);
GL.GetProgram(programId, GetProgramParameterName.ValidateStatus, out var validateStatus);
if (validateStatus == 0)
{
var log = GL.GetProgramInfoLog(programId);
throw new ShaderValidationException(log);
}
}
finally
{
GL.DeleteShader(vertexShader);
GL.DeleteShader(fragmentShader);
}
return programId;
}
}
public class ShaderCompilationException(ShaderType type, string message)
: Exception($"Failed to compile {type} shader: {message}")
{
public ShaderType ShaderType { get; } = type;
}
public class ShaderLinkException(string message) : Exception($"Failed to link shader program: {message}");
public class ShaderValidationException(string message) : Exception($"Failed to validate shader program: {message}");