using OpenTK.GLControl;
|
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Mathematics;
|
using System.ComponentModel;
|
using System.Drawing;
|
using System.Windows.Forms;
|
|
namespace Yw.WinFrmUI.HydroL3d
|
{
|
public partial class OpenGLControl : UserControl
|
{
|
public OpenGLControl()
|
{
|
InitializeComponent();
|
this.glControl1.Load += glControl_Load;
|
}
|
|
#region 着色器资源
|
|
private const string VertexShaderSource = @"#version 330 core
|
|
layout(location = 0) in vec3 aPos;
|
layout(location = 1) in vec4 aColor;
|
|
out vec4 fColor;
|
|
uniform mat4 MVP;
|
|
void main()
|
{
|
gl_Position = vec4(aPos, 1) * MVP;
|
fColor = aColor;
|
}
|
";
|
|
private const string FragmentShaderSource = @"#version 330 core
|
|
in vec4 fColor;
|
|
out vec4 oColor;
|
|
void main()
|
{
|
oColor = fColor;
|
}
|
";
|
|
#endregion
|
|
// 坐标轴顶点数据
|
private static readonly Vector3[] AxisVertexData = new Vector3[]
|
{
|
new Vector3(0, 0, 0), new Vector3(2, 0, 0), // X 轴
|
new Vector3(0, 0, 0), new Vector3(0, 2, 0), // Y 轴
|
new Vector3(0, 0, 0), new Vector3(0, 0, 2) // Z 轴
|
};
|
|
// 坐标轴颜色数据
|
private static readonly Color4[] AxisColorData = new Color4[]
|
{
|
Color4.Red, Color4.Red,
|
Color4.Green, Color4.Green,
|
Color4.Blue, Color4.Blue
|
};
|
|
private static readonly int[] AxisIndexData = new int[] { 0, 1, 2, 3, 4, 5 };
|
|
private Matrix4 _projection; // 投影
|
private System.Windows.Forms.Timer _timer = null!;
|
|
private int CubeShader;
|
|
private int AxisVAO;
|
private int AxisEBO;
|
private int AxisPositionBuffer;
|
private int AxisColorBuffer;
|
|
private void glControl_Load(object sender, EventArgs e)
|
{
|
this.glControl1.Resize += glControl_Resize;
|
this.glControl1.Paint += glControl_Paint;
|
_timer = new System.Windows.Forms.Timer();
|
_timer.Tick += (sender, e) =>
|
{
|
RenderGL();
|
};
|
_timer.Interval = 50; // 1000 ms per sec / 50 ms per frame = 20 FPS
|
_timer.Start();
|
|
CubeShader = CompileProgram(VertexShaderSource, FragmentShaderSource);
|
|
ResizeGL();
|
|
// 初始化坐标轴的 VAO 和 VBO
|
AxisVAO = GL.GenVertexArray();
|
GL.BindVertexArray(AxisVAO);
|
|
AxisEBO = GL.GenBuffer();
|
GL.BindBuffer(BufferTarget.ElementArrayBuffer, AxisEBO);
|
GL.BufferData(BufferTarget.ElementArrayBuffer, AxisIndexData.Length * sizeof(int), AxisIndexData, BufferUsageHint.StaticDraw);
|
|
AxisPositionBuffer = GL.GenBuffer();
|
GL.BindBuffer(BufferTarget.ArrayBuffer, AxisPositionBuffer);
|
GL.BufferData(BufferTarget.ArrayBuffer, AxisVertexData.Length * sizeof(float) * 3, AxisVertexData, BufferUsageHint.StaticDraw);
|
|
GL.EnableVertexAttribArray(0);
|
GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, sizeof(float) * 3, 0);
|
|
AxisColorBuffer = GL.GenBuffer();
|
GL.BindBuffer(BufferTarget.ArrayBuffer, AxisColorBuffer);
|
GL.BufferData(BufferTarget.ArrayBuffer, AxisColorData.Length * sizeof(float) * 4, AxisColorData, BufferUsageHint.StaticDraw);
|
|
GL.EnableVertexAttribArray(1);
|
GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, sizeof(float) * 4, 0);
|
}
|
|
private int CompileProgram(string vertexShader, string fragmentShader)
|
{
|
int program = GL.CreateProgram();
|
|
int vert = CompileShader(ShaderType.VertexShader, vertexShader);
|
int frag = CompileShader(ShaderType.FragmentShader, fragmentShader);
|
|
GL.AttachShader(program, vert);
|
GL.AttachShader(program, frag);
|
|
GL.LinkProgram(program);
|
|
GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int success);
|
if (success == 0)
|
{
|
string log = GL.GetProgramInfoLog(program);
|
throw new Exception($"Could not link program: {log}");
|
}
|
|
GL.DetachShader(program, vert);
|
GL.DetachShader(program, frag);
|
|
GL.DeleteShader(vert);
|
GL.DeleteShader(frag);
|
|
return program;
|
|
static int CompileShader(ShaderType type, string source)
|
{
|
int shader = GL.CreateShader(type);
|
|
GL.ShaderSource(shader, source);
|
GL.CompileShader(shader);
|
|
GL.GetShader(shader, ShaderParameter.CompileStatus, out int status);
|
if (status == 0)
|
{
|
string log = GL.GetShaderInfoLog(shader);
|
throw new Exception($"Failed to compile {type}: {log}");
|
}
|
|
return shader;
|
}
|
}
|
|
// 尺寸改变
|
private void glControl_Resize(object sender, EventArgs e)
|
{
|
ResizeGL();
|
}
|
|
// 绘制
|
private void glControl_Paint(object sender, PaintEventArgs e)
|
{
|
RenderGL();
|
}
|
|
// 渲染
|
private void RenderGL()
|
{
|
this.glControl1.MakeCurrent();
|
|
GL.ClearColor(Color4.Transparent);
|
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
|
GL.Enable(EnableCap.DepthTest); // 开启深度测试
|
|
Matrix4 lookat = Matrix4.LookAt(0, 5, 5, 0, 0, 0, 0, 1, 0);
|
Matrix4 model = Matrix4.Identity;
|
|
Matrix4 mvp = model * lookat * _projection;
|
|
GL.UseProgram(CubeShader);
|
GL.UniformMatrix4(GL.GetUniformLocation(CubeShader, "MVP"), true, ref mvp);
|
|
// 绘制坐标轴
|
GL.BindVertexArray(AxisVAO);
|
GL.DrawElements(BeginMode.Lines, AxisIndexData.Length, DrawElementsType.UnsignedInt, 0);
|
|
this.glControl1.SwapBuffers();
|
}
|
|
// 调整大小
|
private void ResizeGL()
|
{
|
this.glControl1.MakeCurrent();
|
if (this.glControl1.ClientSize.Height == 0)
|
{
|
this.glControl1.ClientSize = new Size(this.glControl1.ClientSize.Width, 1);
|
}
|
GL.Viewport(0, 0, this.glControl1.ClientSize.Width, this.glControl1.ClientSize.Height);
|
float aspect = Math.Max(this.glControl1.ClientSize.Width, 1) / (float)Math.Max(this.glControl1.ClientSize.Height, 1);
|
_projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspect, 1, 64);
|
}
|
|
}
|
}
|