- simplified uniforms set
- autogenerated gl interface
This commit is contained in:
firedef 2022-03-04 16:05:00 +03:00
Родитель 6aec0e58cb
Коммит c392814c5a
18 изменённых файлов: 344 добавлений и 167 удалений

Просмотреть файл

@ -23,6 +23,9 @@
<None Update="data\cube.obj">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="data\monkey.obj">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.12" />

Просмотреть файл

@ -10,6 +10,7 @@ using SomeChartsUi.themes.themes;
using SomeChartsUi.ui.elements;
using SomeChartsUi.utils;
using SomeChartsUi.utils.mesh;
using SomeChartsUi.utils.shaders;
using SomeChartsUi.utils.vectors;
using SomeChartsUiAvalonia.controls;
using SomeChartsUiAvalonia.controls.gl;
@ -46,7 +47,9 @@ public static class ElementsExamples {
r.transform = new(new(0, -100), float3.one * 32, 0);
canvas.AddElement(r);
r.GenerateMesh();
r.shader = GlShaders.diffuse;
r.material = new(GlShaders.diffuse);
//r.material.SetProperty("lightCol", new float3(1,0,0));
r.material.SetProperty("shininess", 32);
MeshRenderer r2 = r;
r.beforeRender += () => {
float time = (float)DateTime.Now.TimeOfDay.TotalMilliseconds;

Просмотреть файл

@ -27,7 +27,7 @@ public abstract class ChartsBackendBase {
public abstract void ClearScreen(color col);
public abstract void DrawMesh(Mesh mesh, Shader? shader, RenderableTransform transform);
public abstract void DrawMesh(Mesh mesh, Material? material, RenderableTransform transform);
//public abstract void DestroyObject(RenderableBase obj);

Просмотреть файл

@ -22,7 +22,7 @@ public abstract partial class RenderableBase {
/// <summary>set to true if data updates frequently <br/>default is false <br/>affects on caching in some elements</summary>
public bool isDynamic = false;
public Shader? shader;
public Material? material;
public RenderableBase(ChartsCanvas owner) {
canvas = owner;
@ -32,7 +32,7 @@ public abstract partial class RenderableBase {
public void Render() {
beforeRender();
if (CheckMeshForUpdate()) GenerateMesh();
DrawMesh(shader);
DrawMesh(material);
AfterDraw();
}
//protected abstract void Render();

Просмотреть файл

@ -25,8 +25,8 @@ public abstract partial class RenderableBase {
// protected void DrawVertices(float2[] points, float2[]? uvs, color[]? colors, ushort[] indexes) =>
// renderer.backend.DrawMesh(points, uvs, colors, indexes, transform.Get(this));
protected void DrawMesh(Shader? shader) {
canvas.renderer.backend.DrawMesh(mesh!, shader, transform);
protected void DrawMesh(Material? material) {
canvas.renderer.backend.DrawMesh(mesh!, material, transform);
}
protected void DrawText(string txt, float2 pos, color col, FontData font, float scale = 12) =>

Просмотреть файл

@ -0,0 +1,27 @@
namespace SomeChartsUi.utils.shaders;
public class Material {
public Shader shader;
public List<MaterialProperty> properties = new();
public Material(Shader shader) => this.shader = shader;
public Material(Shader shader, params MaterialProperty[] properties) : this(shader) => this.properties = properties.ToList();
public Material(Shader shader, params (string name, object val)[] properties) : this(shader) => this.properties = properties.Select(v => new MaterialProperty(v.name, v.val)).ToList();
public void SetProperty<T>(string name, T v) {
int index = properties.FindIndex(p => p.name == name);
if (index == -1) properties.Add(new(name, v!));
else properties[index].value = v!;
}
}
public class MaterialProperty {
public readonly string name;
public object value;
public MaterialProperty(string name, object value) {
this.name = name;
this.value = value;
}
}

Просмотреть файл

@ -5,6 +5,8 @@ public class Shader {
public string vertexShaderSrc;
public string fragmentShaderSrc;
public ShaderUniform[] uniforms = Array.Empty<ShaderUniform>();
public Shader(string name, string vertexShaderSrc, string fragmentShaderSrc) {
this.name = name;
this.vertexShaderSrc = vertexShaderSrc;

Просмотреть файл

@ -0,0 +1,17 @@
namespace SomeChartsUi.utils.shaders;
public readonly struct ShaderUniform {
public readonly string name;
public readonly int location = -1;
public readonly int type;
public readonly int size;
public ShaderUniform(string name, int location, int type, int size) {
this.name = name;
this.location = location;
this.type = type;
this.size = size;
}
public override string ToString() => $"(loc:{location}, name:'{name}', type:{type}, size:{size})";
}

Просмотреть файл

@ -14,6 +14,10 @@
</PropertyGroup>
<ItemGroup>
<None Remove=".gitignore" />
<None Update="src\controls\gl\GlInterfaceGen.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>GlInterfaceGen.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.12" />
@ -31,4 +35,11 @@
<ItemGroup>
<ProjectReference Include="..\SomeChartsUi\SomeChartsUi.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="src\controls\gl\GlInterfaceGen.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>GlInterfaceGen.tt</DependentUpon>
</Compile>
</ItemGroup>
</Project>

Просмотреть файл

@ -40,7 +40,7 @@ public class GlChartsBackend : ChartsBackendBase {
gl.Clear(GlConsts.GL_COLOR_BUFFER_BIT | GlConsts.GL_DEPTH_BUFFER_BIT);
}
public override void DrawMesh(Mesh mesh, Shader? shader, RenderableTransform transform) {
public override void DrawMesh(Mesh mesh, Material? material, RenderableTransform transform) {
if (mesh is not GlMesh obj) throw new NotImplementedException("opengl backend support only GlMesh mesh type; use CreateMesh() in ChartsBackendBase");
GlMesh.gl = gl;
@ -56,14 +56,14 @@ public class GlChartsBackend : ChartsBackendBase {
float3 camPos = owner.transform.position.animatedValue;
//Console.WriteLine(camPos);
Matrix4x4 view = Matrix4x4.CreateLookAt(new(camPos.x, -camPos.y, z), new(camPos.x,-camPos.y,0), new(0, 1, 0));
Matrix4x4 view = Matrix4x4.CreateLookAt(new(-camPos.x, camPos.y, z), new(-camPos.x,camPos.y,0), new(0, -1, 0));
float3 p = transform.position;
// Matrix4x4 model = Matrix4x4.CreateScale(new Vector3(transform.scale.x, transform.scale.y, transform.scale.z), new(p.x, -p.y, p.z));
Matrix4x4 model = Matrix4x4.CreateFromYawPitchRoll(transform.rotation.x, transform.rotation.y, transform.rotation.z) * Matrix4x4.CreateScale(new Vector3(transform.scale.x, -transform.scale.y, transform.scale.z)) * Matrix4x4.CreateTranslation(new(p.x, -p.y, p.z));
Matrix4x4 model = Matrix4x4.CreateFromYawPitchRoll(transform.rotation.x, transform.rotation.y, transform.rotation.z) * Matrix4x4.CreateScale(new Vector3(transform.scale.x, transform.scale.y, transform.scale.z)) * Matrix4x4.CreateTranslation(new(p.x, -p.y, p.z));
// Matrix4x4 model = Matrix4x4.CreateTranslation(new(p.x, -p.y, p.z));
obj.Render(shader, model, view, projection, camPos);
obj.Render(material, model, view, projection, camPos);
}
public override Mesh CreateMesh() => new GlMesh();

Просмотреть файл

@ -86,7 +86,7 @@ public class SkiaChartsBackend : ChartsBackendBase, IDisposable {
public override void ClearScreen(color col) {
throw new NotImplementedException();
}
public override void DrawMesh(Mesh mesh, Shader? shader, RenderableTransform transform) {
public override void DrawMesh(Mesh mesh, Material? material, RenderableTransform transform) {
throw new NotImplementedException();
}
// public override void DrawRect(rect rectangle, color color) {

Просмотреть файл

@ -20,7 +20,7 @@ public class AvaloniaGlCanvasUiController : CanvasUiControllerBase {
public override void OnKey(keycode key, keymods mods) {
base.OnKey(key, mods);
if (key == keycode.y) GlMesh.wireframeMode = !GlMesh.wireframeMode;
if (key == keycode.y) AvaloniaGlChartsCanvas.polygonMode = (PolygonMode)(((int)AvaloniaGlChartsCanvas.polygonMode + 1) % 3);
if (key == keycode.p) GlChartsBackend.perspectiveMode = !GlChartsBackend.perspectiveMode;
}
}

Просмотреть файл

@ -26,10 +26,18 @@ using static Avalonia.OpenGL.GlConsts;
namespace SomeChartsUiAvalonia.controls.gl;
public enum PolygonMode {
fill,
line,
points
}
public class AvaloniaGlChartsCanvas : OpenGlControlBase {
private GlExtrasInterface _glExtras;
public readonly ChartsCanvas canvas = CreateCanvas();
public static PolygonMode polygonMode = PolygonMode.fill;
/// <summary>pause redraw loop</summary>
public bool stopRender;
@ -66,6 +74,7 @@ public class AvaloniaGlChartsCanvas : OpenGlControlBase {
GlMesh.glExtras = _glExtras;
GlShader.glVersion = GlVersion;
GlShader.gl = gl;
GlShader.glExtras = _glExtras;
GlChartsBackend.gl = gl;
}
@ -81,8 +90,13 @@ public class AvaloniaGlChartsCanvas : OpenGlControlBase {
gl.Enable(GL_DEPTH_TEST);
gl.Enable(GL_MULTISAMPLE);
gl.Enable(GL_CULL_FACE);
_glExtras.CullFace(GL_FRONT);
switch (polygonMode) {
case PolygonMode.fill: _glExtras.PolygonMode(GL_FRONT_AND_BACK, GL_FILL); break;
case PolygonMode.line: _glExtras.PolygonMode(GL_FRONT_AND_BACK, GL_LINE); break;
case PolygonMode.points: _glExtras.PolygonMode(GL_FRONT_AND_BACK, GL_POINT); break;
}
//gl.Enable(GL_CULL_FACE);
//_glExtras.CullFace(GL_FRONT);
gl.Viewport(0, 0, (int)Bounds.Width, (int)Bounds.Height);
@ -371,52 +385,3 @@ void main() {
}
");*/
}
public class GlExtrasInterface : GlInterfaceBase<GlInterface.GlContextInfo>
{
public delegate void GlDeleteVertexArrays(int count, int[] buffers);
public delegate void GlBindVertexArray(int array);
public delegate void GlGenVertexArrays(int n, int[] rv);
public unsafe delegate void GlBufferSubData(int trgt, nint offset, nint size, void* data);
public delegate void GlCullFace(int n);
public delegate void GlUniform2f(int location, float x, float y);
public delegate void GlUniform3f(int location, float x, float y, float z);
public delegate void GlUniform4f(int location, float x, float y, float z, float w);
public GlExtrasInterface(GlInterface gl) : base(gl.GetProcAddress, gl.ContextInfo) { }
[GlMinVersionEntryPoint("glDeleteVertexArrays", 3,0)]
[GlExtensionEntryPoint("glDeleteVertexArraysOES", "GL_OES_vertex_array_object")]
public GlDeleteVertexArrays DeleteVertexArrays { get; } = null!;
[GlMinVersionEntryPoint("glBindVertexArray", 3,0)]
[GlExtensionEntryPoint("glBindVertexArrayOES", "GL_OES_vertex_array_object")]
public GlBindVertexArray BindVertexArray { get; } = null!;
[GlMinVersionEntryPoint("glGenVertexArrays",3,0)]
[GlExtensionEntryPoint("glGenVertexArraysOES", "GL_OES_vertex_array_object")]
public GlGenVertexArrays GenVertexArrays { get; } = null!;
[GlEntryPoint("glCullFace")]
public GlCullFace CullFace { get; } = null!;
[GlEntryPoint("glUniform2f")]
public GlUniform2f Uniform2f { get; } = null!;
[GlEntryPoint("glUniform3f")]
public GlUniform3f Uniform3f { get; } = null!;
[GlEntryPoint("glUniform4f")]
public GlUniform4f Uniform4f { get; } = null!;
[GlEntryPoint("glBufferSubData")]
public GlBufferSubData BufferSubData { get; } = null!;
public int GenVertexArray()
{
int[] rv = new int[1];
GenVertexArrays(1, rv);
return rv[0];
}
}

Просмотреть файл

@ -0,0 +1,49 @@
using Avalonia.OpenGL;
namespace SomeChartsUiAvalonia.controls.gl;
public class GlExtrasInterface : GlInterfaceBase<GlInterface.GlContextInfo>
{
public GlExtrasInterface(GlInterface gl) : base(gl.GetProcAddress, gl.ContextInfo) { }
[GlEntryPoint("glDeleteVertexArrays")]
public GlDeleteVertexArrays DeleteVertexArrays { get; } = null!;
public unsafe delegate void GlDeleteVertexArrays(int count, int[] buffers);
[GlEntryPoint("glBindVertexArray")]
public GlBindVertexArray BindVertexArray { get; } = null!;
public unsafe delegate void GlBindVertexArray(int array);
[GlEntryPoint("glGenVertexArrays")]
public GlGenVertexArrays GenVertexArrays { get; } = null!;
public unsafe delegate void GlGenVertexArrays(int count, int[] arrays);
[GlEntryPoint("glBufferSubData")]
public GlBufferSubData BufferSubData { get; } = null!;
public unsafe delegate void GlBufferSubData(int buffer, int offset, int size, void* data);
[GlEntryPoint("glCullFace")]
public GlCullFace CullFace { get; } = null!;
public unsafe delegate void GlCullFace(int face);
[GlEntryPoint("glUniform2f")]
public GlUniform2f Uniform2f { get; } = null!;
public unsafe delegate void GlUniform2f(int location, float x, float y);
[GlEntryPoint("glUniform3f")]
public GlUniform3f Uniform3f { get; } = null!;
public unsafe delegate void GlUniform3f(int location, float x, float y, float z);
[GlEntryPoint("glUniform4f")]
public GlUniform4f Uniform4f { get; } = null!;
public unsafe delegate void GlUniform4f(int location, float x, float y, float z, float w);
[GlEntryPoint("glGetActiveUniform")]
public GlGetActiveUniform GetActiveUniform { get; } = null!;
public unsafe delegate void GlGetActiveUniform(int program, int index, int bufferSize, int* length, int* size, int* type, sbyte* name);
[GlEntryPoint("glPolygonMode")]
public GlPolygonMode PolygonMode { get; } = null!;
public unsafe delegate void GlPolygonMode(int face, int mode);
}

Просмотреть файл

@ -0,0 +1,43 @@
<#@ template language="C#v3.5" #>
<#@ import namespace="System"#>
<#@ import namespace="System.Text"#>
<#@ import namespace="System.Collections"#>
<#@ import namespace="System.Collections.Generic"#>
<#@ assembly name="System.Core"#>
<#@ import namespace="System.Linq"#>
<#
var functions = new[] {
new{name = "DeleteVertexArrays", ret = "void", args = "int count, int[] buffers"},
new{name = "BindVertexArray", ret = "void", args = "int array"},
new{name = "GenVertexArrays", ret = "void", args = "int count, int[] arrays"},
new{name = "BufferSubData", ret = "void", args = "int buffer, int offset, int size, void* data"},
new{name = "CullFace", ret = "void", args = "int face"},
new{name = "Uniform2f", ret = "void", args = "int location, float x, float y"},
new{name = "Uniform3f", ret = "void", args = "int location, float x, float y, float z"},
new{name = "Uniform4f", ret = "void", args = "int location, float x, float y, float z, float w"},
new{name = "GetActiveUniform", ret = "void", args = "int program, int index, int bufferSize, int* length, int* size, int* type, sbyte* name"},
new{name = "PolygonMode", ret = "void", args = "int face, int mode"},
};
#>
using Avalonia.OpenGL;
namespace SomeChartsUiAvalonia.controls.gl;
public class GlExtrasInterface : GlInterfaceBase<GlInterface.GlContextInfo>
{
public GlExtrasInterface(GlInterface gl) : base(gl.GetProcAddress, gl.ContextInfo) { }
<#
foreach (var function in functions) {
#>
[GlEntryPoint("<#=$"gl{function.name}"#>")]
public <#=$"Gl{function.name}"#> <#=function.name#> { get; } = null!;
public unsafe delegate <#=function.ret#> <#=$"Gl{function.name}"#>(<#=function.args#>);
<#
}
#>
}

Просмотреть файл

@ -0,0 +1,135 @@
using System;
using System.Linq;
using System.Numerics;
using System.Runtime.InteropServices;
using Avalonia.OpenGL;
using SomeChartsUi.utils.shaders;
using SomeChartsUi.utils.vectors;
using SomeChartsUiAvalonia.controls.gl;
namespace SomeChartsUiAvalonia.utils;
public class GlShader : Shader {
public static GlVersion glVersion;
public static GlInterface? gl;
public static GlExtrasInterface? glExtras;
public int vertexShader;
public int fragmentShader;
public int shaderProgram;
public void TryCompile() {
if (gl == null) return;
GetUniforms();
vertexShader = gl.CreateShader(GlConsts.GL_VERTEX_SHADER);
Console.WriteLine(gl.CompileShaderAndGetError(vertexShader, vertexShaderSrc));
fragmentShader = gl.CreateShader(GlConsts.GL_FRAGMENT_SHADER);
Console.WriteLine(gl.CompileShaderAndGetError(fragmentShader, fragmentShaderSrc));
shaderProgram = gl.CreateProgram();
gl.AttachShader(shaderProgram, vertexShader);
gl.AttachShader(shaderProgram, fragmentShader);
const int posLoc = 0;
const int normalLoc = 1;
const int uvLoc = 2;
const int colLoc = 3;
gl.BindAttribLocationString(shaderProgram, posLoc, "pos");
gl.BindAttribLocationString(shaderProgram, normalLoc, "normal");
gl.BindAttribLocationString(shaderProgram, uvLoc, "uv");
gl.BindAttribLocationString(shaderProgram, colLoc, "col");
Console.WriteLine(gl.LinkProgramAndGetError(shaderProgram));
GetUniforms();
}
public static string ProcessShader(bool isFragment, string shader) {
bool isOsX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
bool isOpenGles = glVersion.Type == GlProfileType.OpenGLES;
shader = shader.Replace("// ADD ATTRIBUTES", @"
attribute vec3 pos;
attribute vec3 normal;
attribute vec2 uv;
attribute vec4 col;")
.Replace("// ADD MATRICES", @"
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;");
int version = !isOpenGles ? isOsX ? 150 : 120 : 100;
string data = "#version " + version + "\n";
if (isOpenGles) data += "precision mediump float;\n";
if (version >= 150)
{
shader = shader.Replace("attribute", "in");
if (isFragment)
shader = shader
.Replace("varying", "in")
.Replace("// DECLAREGLFRAG", "out vec4 outFragColor;")
.Replace("gl_FragColor", "outFragColor");
else
shader = shader.Replace("varying", "out");
}
data += shader.Replace("// PROCESS VERTEX", "").Replace("// PROCESS FRAGMENT", "");
return data;
}
public GlShader(string name, string vertexShaderSrc, string fragmentShaderSrc) : base(name, vertexShaderSrc, fragmentShaderSrc) {
if (vertexShaderSrc.Trim().StartsWith("// PROCESS VERTEX")) this.vertexShaderSrc = ProcessShader(false, vertexShaderSrc);
if (fragmentShaderSrc.Trim().StartsWith("// PROCESS FRAGMENT")) this.fragmentShaderSrc = ProcessShader(true, fragmentShaderSrc);
}
private unsafe void GetUniforms() {
int uniformCount = 0;
gl!.GetProgramiv(shaderProgram, GlConsts.GL_ACTIVE_UNIFORMS, &uniformCount);
const int stringBufferSize = 32;
sbyte* namePtr = stackalloc sbyte[stringBufferSize];
int size = 0; // size of var
int length = 0; // name length
int type = 0; // type of var
uniforms = new ShaderUniform[uniformCount];
for (int i = 0; i < uniformCount; i++) {
glExtras!.GetActiveUniform(shaderProgram, i, stringBufferSize, &length, &size, &type, namePtr);
string varName = new(namePtr, 0, length);
uniforms[i] = new(varName, i, type, size);
}
}
public unsafe void TrySetUniform<T>(string uniformName, T v) {
int loc = uniforms.FirstOrDefault(u => u.name == uniformName).location;
if (loc == -1) return;
gl!.UseProgram(shaderProgram);
switch (v) {
case float v0: gl.Uniform1f(loc, v0); return;
case float2 v0: glExtras!.Uniform2f(loc, v0.x, v0.y); return;
case float3 v0: glExtras!.Uniform3f(loc, v0.x, v0.y, v0.z); return;
case float4 v0: glExtras!.Uniform4f(loc, v0.x, v0.y, v0.z, v0.w); return;
case Matrix4x4 v0: gl.UniformMatrix4fv(loc, 1, false, &v0); return;
case object obj:
switch (obj) {
case float v0: gl.Uniform1f(loc, v0); return;
case float2 v0: glExtras!.Uniform2f(loc, v0.x, v0.y); return;
case float3 v0: glExtras!.Uniform3f(loc, v0.x, v0.y, v0.z); return;
case float4 v0: glExtras!.Uniform4f(loc, v0.x, v0.y, v0.z, v0.w); return;
case Matrix4x4 v0: gl.UniformMatrix4fv(loc, 1, false, &v0); return;
}
return;
default: throw new NotImplementedException();
}
}
public void TryApplyMaterial(Material mat) {
foreach (MaterialProperty property in mat.properties)
TrySetUniform(property.name, property.value);
}
}

Просмотреть файл

@ -1,9 +1,3 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.OpenGL;
using SomeChartsUi.utils.shaders;
using static Avalonia.OpenGL.GlConsts;
namespace SomeChartsUiAvalonia.utils;
public static class GlShaders {
@ -62,6 +56,7 @@ void main() {
fragPos = vec3(model * vec4(pos,1.0));
fragUv = uv;
fragCol = col;
//fragNormal = normal;
fragNormal = normalize(mat3(transpose(inverse(model))) * normal);
}
", @"
@ -75,98 +70,27 @@ varying vec4 fragCol;
uniform vec3 cameraPos;
uniform vec3 lightDir = normalize(vec3(-0.2,1,0.1));
uniform float time = 0;
uniform vec3 lightDirr = normalize(vec3(10,-1,-1));
uniform float lightIntensity = 2;
uniform vec3 lightCol = vec3(1, 0.6, 0.4);
uniform vec3 ambientCol = vec3(0.1, 0.1, 0.15);
uniform vec3 lightCol = vec3(255/255.0, 139/255.0, 73/255.0);
uniform vec3 ambientCol = vec3(6/255.0, 12/255.0, 45/255.0);
uniform vec3 diffuseCol = vec3(0.7, 0.7, 0.8);
uniform float specularIntensity = 0.5;
uniform float shininess = 4;
uniform float specularIntensity = 2.5;
void main() {
vec3 lightDir = normalize(vec3(sin(time * 0.001) * 10, sin(time * 0.0001) * 10, sin(time * 0.0005) * 20));
vec3 viewDir = normalize(cameraPos - fragPos);
vec3 reflectDir = reflect(-lightDir, fragNormal);
float specularStrength = pow(max(dot(viewDir, reflectDir), 0.0), 32);
vec3 halfwayDir = normalize(lightDir + viewDir);
float specularStrength = pow(max(dot(fragNormal, halfwayDir), 0.0), shininess);
vec3 specular = specularIntensity * specularStrength * lightCol;
float diff = max(dot(fragNormal,lightDir), 0);
vec3 diffuse = diff * lightCol * diffuseCol * lightIntensity;
vec3 diffuse = diff * lightCol * lightIntensity;
gl_FragColor = vec4(diffuse + ambientCol + specular, 1);
}
");
}
public class GlShader : Shader {
public static GlVersion glVersion;
public static GlInterface? gl;
public int vertexShader;
public int fragmentShader;
public int shaderProgram;
public void TryCompile() {
if (gl == null) return;
vertexShader = gl.CreateShader(GL_VERTEX_SHADER);
Console.WriteLine(gl.CompileShaderAndGetError(vertexShader, vertexShaderSrc));
fragmentShader = gl.CreateShader(GL_FRAGMENT_SHADER);
Console.WriteLine(gl.CompileShaderAndGetError(fragmentShader, fragmentShaderSrc));
shaderProgram = gl.CreateProgram();
gl.AttachShader(shaderProgram, vertexShader);
gl.AttachShader(shaderProgram, fragmentShader);
const int posLoc = 0;
const int normalLoc = 1;
const int uvLoc = 2;
const int colLoc = 3;
gl.BindAttribLocationString(shaderProgram, posLoc, "pos");
gl.BindAttribLocationString(shaderProgram, normalLoc, "normal");
gl.BindAttribLocationString(shaderProgram, uvLoc, "uv");
gl.BindAttribLocationString(shaderProgram, colLoc, "col");
Console.WriteLine(gl.LinkProgramAndGetError(shaderProgram));
}
public static string ProcessShader(bool isFragment, string shader) {
bool isOsX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
bool isOpenGles = glVersion.Type == GlProfileType.OpenGLES;
shader = shader.Replace("// ADD ATTRIBUTES", @"
attribute vec3 pos;
attribute vec3 normal;
attribute vec2 uv;
attribute vec4 col;")
.Replace("// ADD MATRICES", @"
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;");
int version = !isOpenGles ? isOsX ? 150 : 120 : 100;
string data = "#version " + version + "\n";
if (isOpenGles) data += "precision mediump float;\n";
if (version >= 150)
{
shader = shader.Replace("attribute", "in");
if (isFragment)
shader = shader
.Replace("varying", "in")
.Replace("// DECLAREGLFRAG", "out vec4 outFragColor;")
.Replace("gl_FragColor", "outFragColor");
else
shader = shader.Replace("varying", "out");
}
data += shader.Replace("// PROCESS VERTEX", "").Replace("// PROCESS FRAGMENT", "");
return data;
}
public GlShader(string name, string vertexShaderSrc, string fragmentShaderSrc) : base(name, vertexShaderSrc, fragmentShaderSrc) {
if (vertexShaderSrc.Trim().StartsWith("// PROCESS VERTEX")) this.vertexShaderSrc = ProcessShader(false, vertexShaderSrc);
if (fragmentShaderSrc.Trim().StartsWith("// PROCESS FRAGMENT")) this.fragmentShaderSrc = ProcessShader(true, fragmentShaderSrc);
}
}

Просмотреть файл

@ -32,7 +32,8 @@ public class GlMesh : Mesh {
gl.GenBuffers(2, buffers);
vertexBufferObject = buffers[0];
indexBufferObject = buffers[1];
vertexArrayObject = glExtras.GenVertexArray();
glExtras.GenVertexArrays(1, buffers);
vertexArrayObject = buffers[0];
BindBuffers();
@ -95,27 +96,24 @@ public class GlMesh : Mesh {
public override void OnModified() => updateRequired = true;
public unsafe void Render(Shader? shader, Matrix4x4 model, Matrix4x4 view, Matrix4x4 projection, float3 cameraPos) {
public unsafe void Render(Material? material, Matrix4x4 model, Matrix4x4 view, Matrix4x4 projection, float3 cameraPos) {
if (vertexArrayObject == 0) GenBuffers();
if (vertexArrayObject == 0) return;
if (shader != null && shader is not GlShader) return;
if (material is {shader: not GlShader}) return;
if (updateRequired | isDynamic) UpdateBuffers();
GlShader shaderData = shader == null ? GlShaders.basic : (GlShader) shader;
if (shaderData.shaderProgram == 0) shaderData.TryCompile();
if (shaderData.shaderProgram == 0) return;
GlShader shader = material == null ? GlShaders.basic : (GlShader) material.shader;
if (shader.shaderProgram == 0) shader.TryCompile();
if (shader.shaderProgram == 0) return;
gl!.UseProgram(shaderData.shaderProgram);
gl!.UseProgram(shader.shaderProgram);
int modelLoc = gl.GetUniformLocationString(shaderData.shaderProgram, "model");
int viewLoc = gl.GetUniformLocationString(shaderData.shaderProgram, "view");
int projectionLoc = gl.GetUniformLocationString(shaderData.shaderProgram, "projection");
int cameraLoc = gl.GetUniformLocationString(shaderData.shaderProgram, "cameraPos");
gl.UniformMatrix4fv(modelLoc, 1, false, &model);
gl.UniformMatrix4fv(viewLoc, 1, false, &view);
gl.UniformMatrix4fv(projectionLoc, 1, false, &projection);
if (cameraLoc != -1) glExtras!.Uniform3f(cameraLoc, cameraPos.x, cameraPos.y, cameraPos.z);
shader.TrySetUniform("model", model);
shader.TrySetUniform("view", view);
shader.TrySetUniform("projection", projection);
shader.TrySetUniform("cameraPos", cameraPos);
shader.TrySetUniform("time", (float)DateTime.Now.TimeOfDay.TotalMilliseconds);
if (material != null) shader.TryApplyMaterial(material);
BindBuffers();
gl.DrawElements(wireframeMode ? GL_LINES : GL_TRIANGLES, indexes.count, GL_UNSIGNED_SHORT, IntPtr.Zero);