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); } } }