using System; using System.Drawing; using System.Collections.Generic; using System.Linq; using OpenTK; using OpenTK.Graphics.OpenGL; using System.Windows.Forms; using System.Drawing.Imaging; using OpenTK.Mathematics; using OpenTK.GLControl; using System.Drawing.Text; using System.Security.Policy; namespace Yw.WinFrmUI.Test.Core { public partial class Form1 : Form { public Form1() { InitializeComponent(); this.glControl1.Load += GlControl1_Load; this.glControl1.Paint += GlControl1_Paint; this.glControl1.Resize += GlControl1_Resize; this.glControl1.MouseDown += GlControl1_MouseDown; this.glControl1.MouseUp += GlControl1_MouseUp; this.glControl1.MouseWheel += GlControl1_MouseWheel; this.glControl1.MouseMove += GlControl1_MouseMove; } private void GlControl1_MouseMove(object sender, MouseEventArgs e) { HandleMouseMove(e); } private void GlControl1_MouseWheel(object sender, MouseEventArgs e) { HandleMouseWheel(e); } private void GlControl1_MouseUp(object sender, MouseEventArgs e) { HandleMouseUp(e); } private void GlControl1_MouseDown(object sender, MouseEventArgs e) { HandleMouseDown(e); } private void GlControl1_Resize(object sender, EventArgs e) { OnResize(); } private void GlControl1_Paint(object sender, PaintEventArgs e) { OnRender(); } private void GlControl1_Load(object sender, EventArgs e) { InitialGLState(); } #region 字段声明 private const float _size = 100f; private Matrix4 _modelMatrix = Matrix4.Identity; private Matrix4 _viewMatrix = Matrix4.Identity; private Matrix4 _projMatrix = Matrix4.Identity; private float _rotationX, _rotationY; private float _translationX, _translationY; private float _zoom = 1f; private bool _isDragging, _isRotating; private Point _lastMousePos; private float _radius = 100f; private float _pw, _ph; private Vector3 _mc = new Vector3(0f); private Vector3 _eye, _target, _up; private readonly int[] _textures = new int[6]; private float _screenWidth, _screenHeight; public float Zoom { get => _zoom; set => _zoom = MathHelper.Clamp(value, 0.01f, 100.0f); // 限制缩放范围 } #endregion #region OpenGL初始化 //初始化GL状态 private void InitialGLState() { GL.ClearColor(Color.Transparent); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.Texture2D); GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); // 生成六个面的文字纹理 _textures[0] = CreateTextTexture("前", 128, Color.White, Color.Red); _textures[1] = CreateTextTexture("后", 128, Color.White, Color.Green); _textures[2] = CreateTextTexture("左", 128, Color.White, Color.Blue); _textures[3] = CreateTextTexture("右", 128, Color.White, Color.Yellow); _textures[4] = CreateTextTexture("上", 128, Color.White, Color.Magenta); _textures[5] = CreateTextTexture("下", 128, Color.White, Color.Cyan); } #endregion #region 矩阵更新 private void UpdateMatrices() { if (!this.glControl1.IsHandleCreated) { return; } UpdateViewport(); UpdateProjection(); UpdateView(); UpdateModel(); } //更新视口 private void UpdateViewport() { if (!this.glControl1.IsHandleCreated) { return; } _screenWidth = this.glControl1.Width; _screenHeight = this.glControl1.Height; GL.Viewport(0, 0, this.glControl1.Width, this.glControl1.Height); } private void UpdateProjection() { if (!this.glControl1.IsHandleCreated) { return; } // 获取窗口尺寸 int screenWidth = ClientSize.Width; int screenHeight = ClientSize.Height; // 设置中心对齐的正交投影 Matrix4 projection = Matrix4.CreateOrthographicOffCenter( left: -screenWidth / 2f, right: screenWidth / 2f, bottom: -screenHeight / 2f, top: screenHeight / 2f, depthNear: -1, depthFar: 1 ); _projMatrix = projection; //var aspect = this.glControl1.AspectRatio; //var size = _radius * 2; //_pw = size; //_ph = size; //if (aspect > 1) //{ // _pw *= aspect; //} //else //{ // _ph /= aspect; //} //_projMatrix = Matrix4.CreateOrthographic(_pw * this.Zoom, _ph * this.Zoom, _radius, _radius * 3); } private void UpdateView() { _eye = new Vector3(_mc.X, _mc.Y, _mc.Z + 2 * _radius); _target = _mc; _up = Vector3.UnitY; //var rotation = Quaternion.Identity; //var rotationX = Quaternion.FromAxisAngle(Vector3.UnitX, _rotationX); //var rotationY = Quaternion.FromAxisAngle(Vector3.UnitY, _rotationY); // rotation = rotationY * rotationX * rotation; //rotation.Normalize(); // var matrix = Matrix4.CreateFromQuaternion(rotation); // matrix.Invert(); // eye = Vector3.TransformPosition(eye, matrix); // up = Vector3.TransformPosition(up, matrix); _viewMatrix = Matrix4.LookAt(_eye, _target, _up); } private void UpdateModel() { _modelMatrix = Matrix4.CreateTranslation(new Vector3(_pw / 2f * this.Zoom, _ph / 2f * this.Zoom, 0)) * Matrix4.CreateFromAxisAngle(Vector3.UnitX, _rotationX) * Matrix4.CreateFromAxisAngle(Vector3.UnitY, _rotationY) * Matrix4.CreateTranslation(-new Vector3(_pw / 2f * this.Zoom, _ph / 2f * this.Zoom, 0)) * Matrix4.CreateTranslation(_translationX * this.Zoom, _translationY * this.Zoom, 0f); } #endregion #region 渲染逻辑 private void OnRender() { if (!this.glControl1.IsHandleCreated) { return; } GL.Clear(ClearBufferMask.ColorBufferBit); GL.MatrixMode(MatrixMode.Modelview); GL.LoadIdentity(); DrawReferenceLines(); DrawRotatingObject(); //SwapBuffers(); //this.glControl1.MakeCurrent(); //GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); //// 设置矩阵 //GL.MatrixMode(MatrixMode.Projection); //GL.LoadMatrix(ref _projMatrix); //GL.MatrixMode(MatrixMode.Modelview); //Matrix4 modelView = _viewMatrix * _modelMatrix; //GL.LoadMatrix(ref modelView); //// 绘制立方体 ////DrawCube(); //// 绘制立方体 //DrawTexturedCube(); //var error = GL.GetError(); //// 绘制标签 ////DrawLabels(); //this.glControl1.SwapBuffers(); } float rotationAngle = 45; // 测试角度 void DrawRotatingObject() { GL.PushMatrix(); // 关键变换顺序 GL.Translate(100, 50, 0); // 将物体移动到屏幕坐标系中的(100,50) GL.Rotate(rotationAngle, Vector3.UnitZ); // 绕当前原点(屏幕中心)旋转 // 绘制以原点为中心的物体 GL.Color3(Color.Red); GL.Begin(PrimitiveType.Quads); GL.Vertex2(-25, -25); GL.Vertex2(25, -25); GL.Vertex2(25, 25); GL.Vertex2(-25, 25); GL.End(); GL.PopMatrix(); } void DrawReferenceLines() { //// 绘制屏幕中心十字 //GL.Color3(Color.White); //GL.LineWidth(2); //GL.Begin(PrimitiveType.Lines); //// 水平线 //GL.Vertex2(-1000, 0); GL.Vertex2(1000, 0); //// 垂直线 //GL.Vertex2(0, -1000); GL.Vertex2(0, 1000); //GL.End(); //// 绘制屏幕边框 //GL.Color3(Color.Gray); //GL.Begin(PrimitiveType.LineLoop); //GL.Vertex2(-screenWidth / 2, -screenHeight / 2); //GL.Vertex2(screenWidth / 2, -screenHeight / 2); //GL.Vertex2(screenWidth / 2, screenHeight / 2); //GL.Vertex2(-screenWidth / 2, screenHeight / 2); //GL.End(); } //改变 private void OnResize() { if (!this.glControl1.IsHandleCreated) { return; } this.glControl1.MakeCurrent(); // 防止OpenGL错误 if (this.glControl1.Width == 0) { this.glControl1.Width = 1; } if (this.glControl1.Height == 0) { this.glControl1.Height = 1; } // 更新所有矩阵 UpdateMatrices(); // 请求重绘 this.glControl1.Invalidate(); } private void DrawCube() { GL.Begin(PrimitiveType.Quads); // 前面(Z+) //GL.Color3(Color.Purple); GL.Color4((float)Color.Purple.R, (float)Color.Purple.G, (float)Color.Purple.B, 1f); GL.Vertex3(-50f, -50f, 50f); GL.Vertex3(50f, -50f, 50f); GL.Vertex3(50f, 50f, 50f); //GL.Vertex3(50f, 50f, 50f); GL.Vertex3(-50f, 50f, 50f); // GL.Vertex3(-50f, -50f, 50f); // 后面(Z-) //GL.Color3(Color.Cyan); GL.Color4((float)Color.Cyan.R, (float)Color.Cyan.G, (float)Color.Cyan.B, 1f); GL.Vertex3(-50f, -50f, -50f); GL.Vertex3(-50f, 50f, -50f); GL.Vertex3(50f, 50f, -50f); //GL.Vertex3(50f, 50f, -50f); GL.Vertex3(50f, -50f, -50f); //GL.Vertex3(-50f, -50f, -50f); // 顶面(Y+) //GL.Color3(Color.Lime); GL.Color4((float)Color.Lime.R, (float)Color.Lime.G, (float)Color.Lime.B, 1f); GL.Vertex3(-50f, 50f, -50f); GL.Vertex3(50f, 50f, -50f); GL.Vertex3(50f, 50f, 50f); //GL.Vertex3(50f, 50f, 50f); GL.Vertex3(-50f, 50f, 50f); //GL.Vertex3(-50f, 50f, -50f); DrawTextOnFace("上", new Vector3(0, 50f, 0), new Vector3(0, 0, 100f)); // 底面(Y-) //GL.Color3(Color.Yellow); GL.Color4((float)Color.Yellow.R, (float)Color.Yellow.G, (float)Color.Yellow.B, 1f); GL.Vertex3(-50f, -50f, -50f); GL.Vertex3(50f, -50f, -50f); GL.Vertex3(50f, -50f, 50f); //GL.Vertex3(50f, -50f, 50f); GL.Vertex3(-50f, -50f, 50f); //GL.Vertex3(-50f, -50f, -50f); // 右面(X+) //GL.Color3(Color.Red); GL.Color4((float)Color.Red.R, (float)Color.Red.G, (float)Color.Red.B, 1f); GL.Vertex3(50f, -50f, -50f); GL.Vertex3(50f, 50f, -50f); GL.Vertex3(50f, 50f, 50f); // GL.Vertex3(50f, 50f, 50f); GL.Vertex3(50f, -50f, 50f); //GL.Vertex3(50f, -50f, -50f); // 左面(X-) GL.Color4((float)Color.Blue.R, (float)Color.Blue.G, (float)Color.Blue.B, 1f); //GL.Color3(Color.Blue); GL.Vertex3(-50f, -50f, -50f); GL.Vertex3(-50f, 50f, -50f); GL.Vertex3(-50f, 50f, 50f); //GL.Vertex3(-50f, 50f, 50f); GL.Vertex3(-50f, -50f, 50f); //GL.Vertex3(-50f, -50f, -50f); GL.End(); GL.End(); } private void DrawFace(Vector3 v1, Vector3 v2, Vector3 v3, Vector3 v4, Color4 color, string text) { GL.Begin(PrimitiveType.Quads); GL.Color4(color); GL.Vertex3(v1); GL.Vertex3(v2); GL.Vertex3(v3); GL.Vertex3(v4); GL.End(); Vector3 center = (v1 + v2 + v3 + v4) / 4; Vector3 normal = Vector3.Cross(v2 - v1, v3 - v1); DrawTextOnFace(text, center, normal); } private void DrawTextOnFace(string text, Vector3 position, Vector3 normal) { //GL.PushMatrix(); //GL.Translate(position); //Vector3 up = Vector3.UnitY; //Vector3 right = Vector3.Cross(normal, up); // up = Vector3.Cross(right, normal); // Matrix4 rotation = new Matrix4( // new Vector4(right, 0), // new Vector4(up, 0), // new Vector4(-normal, 0), // new Vector4(0, 0, 0, 1)); //GL.MultMatrix(ref rotation); using (Bitmap bmp = new Bitmap(100, 100)) using (Graphics g = Graphics.FromImage(bmp)) { g.TextRenderingHint = TextRenderingHint.AntiAlias; Font font = new Font("Arial", 12); SizeF textSize = g.MeasureString(text, font); float xOffset = (bmp.Width - textSize.Width) / 2; float yOffset = (bmp.Height - textSize.Height) / 2; g.DrawString(text, font, Brushes.Black, new PointF(xOffset, yOffset)); int textureId = GL.GenTexture(); GL.BindTexture(TextureTarget.Texture2D, textureId); BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0); bmp.UnlockBits(data); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.Enable(EnableCap.Texture2D); GL.Begin(PrimitiveType.Quads); GL.TexCoord2(0, 0); GL.Vertex3(-10, -10, 0); GL.TexCoord2(100, 0); GL.Vertex3(10, -10, 0); GL.TexCoord2(100, 100); GL.Vertex3(10, 10, 0); GL.TexCoord2(0, 100); GL.Vertex3(-10, 10, 0); GL.End(); GL.Disable(EnableCap.Texture2D); GL.DeleteTexture(textureId); } // GL.PopMatrix(); } private int CreateTextTexture(string text, int size, Color textColor, Color bgColor) { // 创建位图并绘制文字 using (var bmp = new Bitmap(size, size)) using (var gfx = Graphics.FromImage(bmp)) { gfx.Clear(bgColor); var font = new Font("宋体", 32, FontStyle.Bold); var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }; gfx.DrawString(text, font, new SolidBrush(textColor), new RectangleF(0, 0, size, size), format); bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); // 转换位图为OpenGL纹理 var data = bmp.LockBits( new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); int texId = GL.GenTexture(); GL.BindTexture(TextureTarget.Texture2D, texId); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0); // 设置纹理参数 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); bmp.UnlockBits(data); return texId; } } private void DrawTexturedCube() { // 前面 (Z+) DrawFace(_textures[0], new[] { new Vector3(-50, -50, 50), new Vector3(50, -50, 50), new Vector3(50, 50, 50), new Vector3(-50, 50, 50) }); // 后面 (Z-) DrawFace(_textures[1], new[] { new Vector3(50, -50, -50), new Vector3(-50, -50, -50), new Vector3(-50, 50, -50), new Vector3(50, 50, -50) }); // 左面 (X-) DrawFace(_textures[2], new[] { new Vector3(-50, -50, -50), new Vector3(-50, -50, 50), new Vector3(-50, 50, 50), new Vector3(-50, 50, -50) }); // 右面 (X+) DrawFace(_textures[3], new[] { new Vector3(50, -50, 50), new Vector3(50, -50, -50), new Vector3(50, 50, -50), new Vector3(50, 50, 50) }); // 顶面 (Y+) DrawFace(_textures[4], new[] { new Vector3(-50, 50, 50), new Vector3(50, 50, 50), new Vector3(50, 50, -50), new Vector3(-50, 50, -50) }); // 底面 (Y-) DrawFace(_textures[5], new[] { new Vector3(-50, -50, -50), new Vector3(50, -50, -50), new Vector3(50, -50, 50), new Vector3(-50, -50, 50) }); } private void DrawFace(int textureId, Vector3[] vertices) { GL.BindTexture(TextureTarget.Texture2D, textureId); GL.Begin(PrimitiveType.Quads); GL.TexCoord2(0, 0); GL.Vertex3(vertices[0]); GL.TexCoord2(1, 0); GL.Vertex3(vertices[1]); GL.TexCoord2(1, 1); GL.Vertex3(vertices[2]); GL.TexCoord2(0, 1); GL.Vertex3(vertices[3]); // 修正后的纹理坐标(翻转Y方向) //GL.TexCoord2(0, 1); GL.Vertex3(vertices[0]); // 左上→左下 //GL.TexCoord2(1, 1); GL.Vertex3(vertices[1]); // 右上→右下 //GL.TexCoord2(1, 0); GL.Vertex3(vertices[2]); // 右下→右上 //GL.TexCoord2(0, 0); GL.Vertex3(vertices[3]); // 左下→左上 GL.End(); } #endregion #region 交互处理 /// /// 处理鼠标滚轮 /// public void HandleMouseWheel(MouseEventArgs e) { if (!this.glControl1.IsHandleCreated) { return; } this.Zoom *= e.Delta > 0 ? 0.9f : 1.1f; UpdateMatrices(); this.glControl1.Invalidate(); } /// /// 处理鼠标按下 /// public void HandleMouseDown(MouseEventArgs e) { if (!this.glControl1.IsHandleCreated) { return; } _lastMousePos = e.Location; if (e.Button == MouseButtons.Right) { _isDragging = true; this.glControl1.Cursor = Cursors.SizeAll; // 修改光标样式 } else if (e.Button == MouseButtons.Left) { _isRotating = true; this.glControl1.Cursor = Cursors.SizeAll; // 修改光标样式 } } /// /// 处理鼠标弹起 /// public void HandleMouseUp(MouseEventArgs e) { if (!this.glControl1.IsHandleCreated) { return; } _lastMousePos = e.Location; if (e.Button == MouseButtons.Right) { _isDragging = false; this.glControl1.Cursor = Cursors.Default; } else if (e.Button == MouseButtons.Left) { _isRotating = false; this.glControl1.Cursor = Cursors.Default; } this.glControl1.Invalidate(); } /// /// 处理鼠标移动 /// public void HandleMouseMove(MouseEventArgs e) { if (!this.glControl1.IsHandleCreated) { return; } float dx = e.X - _lastMousePos.X; float dy = e.Y - _lastMousePos.Y; if (_isRotating) { // 根据鼠标移动量计算旋转角度 var roation_dx = dy / _radius; var rotaion_dy = dx / _radius; _rotationX += roation_dx; _rotationY += rotaion_dy; UpdateMatrices(); } else if (_isDragging) { float xratio = _pw / this.glControl1.Width; float yratio = _ph / this.glControl1.Height; _translationX += dx * xratio; _translationY -= dy * yratio; UpdateMatrices(); } _lastMousePos = e.Location; this.glControl1.Invalidate(); } #endregion } }