using System.Drawing.Imaging; using OpenTK.Graphics.OpenGL; using OpenTK.Mathematics; namespace Yw.WinFrmUI.Test.Core { public partial class TextForm1 : Form { public TextForm1() { this.InitializeComponent(); this.glControl1.Paint += GLControl_Paint; this.glControl1.Resize += GLControl_Resize; this.glControl1.Load += GlControl1_Load; this.glControl1.MouseWheel += GlControl1_MouseWheel; this.glControl1.MouseMove += GlControl1_MouseMove; this.glControl1.MouseDown += GlControl1_MouseDown; this.glControl1.MouseUp += GlControl1_MouseUp; } private void GlControl1_MouseUp(object sender, MouseEventArgs e) { HandleMouseUp(e); } private void GlControl1_MouseDown(object sender, MouseEventArgs e) { HandleMouseDown(e); } private void GlControl1_MouseMove(object sender, MouseEventArgs e) { HandleMouseMove(e); } private void GlControl1_MouseWheel(object sender, MouseEventArgs e) { HandleMouseWheel(e); } private Vector3 _mc = new Vector3(0f); private float _radius = 2f; private float _rotationX = 0f; private float _rotationY = 0f; private Quaternion _rotation = Quaternion.Identity; private float _translationX = 0f; private float _translationY = 0f; private float _zoom = 1f; private Matrix4 _modelMatrix = Matrix4.Identity; private Matrix4 _viewMatrix = Matrix4.Identity; private Matrix4 _projMatrix = Matrix4.Identity; private Vector3 _forward = Vector3.Zero; private float _pw = 1f; private float _ph = 1f; private Dictionary textureCache = new Dictionary(); // 各面配置 private readonly Dictionary faceConfigs = new Dictionary { { "+X", new FaceConfig("右", Color.Red, new Vector3(0.5f, 0, 0)) }, { "-X", new FaceConfig("左", Color.Blue, new Vector3(-0.5f, 0, 0)) }, { "+Y", new FaceConfig("上", Color.Green, new Vector3(0, 0.5f, 0)) }, { "-Y", new FaceConfig("下", Color.Yellow, new Vector3(0, -0.5f, 0)) }, { "+Z", new FaceConfig("前", Color.Purple, new Vector3(0, 0, 0.5f)) }, { "-Z", new FaceConfig("后", Color.Cyan, new Vector3(0, 0, -0.5f)) } }; /// /// 缩放 /// public float Zoom { get => _zoom; set => _zoom = MathHelper.Clamp(value, 0.01f, 100.0f); // 限制缩放范围 } //更新ViewPort private void UpdateViewPort() { GL.Viewport(0, 0, this.glControl1.Width, this.glControl1.Height); } //更新ModelMatrix private void UpdateModelMatrix() { _modelMatrix = Matrix4.CreateFromAxisAngle(Vector3.UnitX, _rotationX) * Matrix4.CreateFromAxisAngle(Vector3.UnitY, _rotationY) * Matrix4.CreateTranslation(_translationX * this.Zoom, _translationY * this.Zoom, 0f); } //更新ViewMatrix private void UpdateViewMatrix() { var eye = new Vector3(_mc.X, _mc.Y, _mc.Z + _radius * 2); var target = _mc; var up = Vector3.UnitY; //_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); _forward = target - eye; _forward.Normalize(); } //更新投影矩阵 private void UpdateProjectionMatrix() { 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 OnResize() { this.glControl1.MakeCurrent(); UpdateViewPort(); UpdateModelMatrix(); UpdateViewMatrix(); UpdateProjectionMatrix(); this.glControl1.Invalidate(); } private void OnRender() { this.glControl1.MakeCurrent(); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit); // 启用必要功能 GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha); //投影矩阵 GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref _projMatrix); //视图矩阵 GL.MatrixMode(MatrixMode.Modelview); var viewMatrix = _viewMatrix * _modelMatrix; GL.LoadMatrix(ref viewMatrix); DrawColoredCube(); foreach (var face in faceConfigs) { DrawFaceLabel(_modelMatrix, face.Key); } this.glControl1.SwapBuffers(); } private void GlControl1_Load(object sender, EventArgs e) { GL.ClearColor(Color.Gray); // 背景颜色 GL.Enable(EnableCap.DepthTest);//深度测试 GL.DepthMask(true); // 允许写入深度缓冲区 GL.ShadeModel(ShadingModel.Smooth); GL.Enable(EnableCap.PointSmooth);//启用点平滑 GL.Enable(EnableCap.LineSmooth);//启用线平滑 PreloadTextures(); OnResize(); } private void GLControl_Resize(object sender, EventArgs e) { OnResize(); } private void GLControl_Paint(object sender, PaintEventArgs e) { OnRender(); } private void PreloadTextures() { foreach (var face in faceConfigs) { using (var bmp = CreateTextBitmap(face.Value.Text, "微软雅黑", 20, Color.White, face.Value.Color)) { int textureId = UploadTexture(bmp); textureCache.Add(face.Key, textureId); } } } private void DrawFaceLabel(Matrix4 modelMatrix, string faceKey) { if (!textureCache.ContainsKey(faceKey)) return; var config = faceConfigs[faceKey]; //GL.PushMatrix(); //GL.MatrixMode(MatrixMode.Projection); //GL.PushMatrix(); // 设置正交投影 //GL.LoadIdentity(); //GL.Ortho(-1, 1, -1, 1, -1, 1); // 计算标签位置 //Matrix4 labelMatrix = modelMatrix * //Matrix4.CreateTranslation(config.Position) * //_viewMatrix; // 移除旋转分量 //labelMatrix.ClearRotation(); // GL.MatrixMode(MatrixMode.Modelview); // GL.LoadMatrix(ref labelMatrix); // 绑定纹理 GL.Enable(EnableCap.Texture2D); GL.BindTexture(TextureTarget.Texture2D, textureCache[faceKey]); // 绘制标签 float scale = 0.15f; GL.Begin(PrimitiveType.Quads); GL.TexCoord2(0, 0); GL.Vertex2(-scale, -scale); GL.TexCoord2(1, 0); GL.Vertex2(scale, -scale); GL.TexCoord2(1, 1); GL.Vertex2(scale, scale); GL.TexCoord2(0, 1); GL.Vertex2(-scale, scale); GL.End(); //GL.PopMatrix(); // GL.MatrixMode(MatrixMode.Projection); // GL.PopMatrix(); } private Bitmap CreateTextBitmap(string text, string fontName, int fontSize, Color textColor, Color bgColor) { using (var font = new Font(fontName, fontSize, FontStyle.Bold, GraphicsUnit.Pixel)) using (var textBrush = new SolidBrush(textColor)) using (var bgBrush = new SolidBrush(bgColor)) { SizeF textSize; using (var tempG = Graphics.FromHwnd(IntPtr.Zero)) textSize = tempG.MeasureString(text, font); int width = (int)Math.Ceiling(textSize.Width) + 4; int height = (int)Math.Ceiling(textSize.Height) + 4; var bmp = new Bitmap(width, height); using (var g = Graphics.FromImage(bmp)) { g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias; g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit; // 绘制背景 g.FillRectangle(bgBrush, 0, 0, width, height); // 居中绘制文字 float x = (width - textSize.Width) / 2; float y = (height - textSize.Height) / 2; g.DrawString(text, font, textBrush, x, y); } return bmp; } } //private Matrix4 CalculateBillboardMatrix(Vector3 position, Matrix4 modelMatrix) //{ // // 获取世界空间位置 // Vector3 worldPos = Vector3.Transform(position, modelMatrix); // // 计算面向相机的旋转 // Matrix4 viewRotation = viewMatrix.ClearTranslation(); // Matrix4 inverseViewRotation = Matrix4.Transpose(viewRotation); // return inverseViewRotation * Matrix4.CreateTranslation(worldPos); //} private int UploadTexture(Bitmap bmp) { this.glControl1.MakeCurrent(); 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); return textureId; } public class FaceConfig { public string Text { get; } public Color Color { get; } public Vector3 Position { get; } public FaceConfig(string text, Color color, Vector3 position) { Text = text; Color = color; Position = position; } } //颜色字典 private readonly static Dictionary _colorDict = new Dictionary() { { "右",ColorTranslator.FromHtml("#FF0000")}, { "左",ColorTranslator.FromHtml("#87CEEB")}, { "后",ColorTranslator.FromHtml("#008000")}, { "前",ColorTranslator.FromHtml("#00008B")}, { "上",ColorTranslator.FromHtml("#800080")}, { "下",ColorTranslator.FromHtml("#242424")} }; //绘制彩色立方体 private void DrawColoredCube() { GL.Begin(PrimitiveType.Quads); // 前面(Z+) GL.Color3(_colorDict["前"]); GL.Vertex3(-0.5f, -0.5f, 0.5f); GL.Vertex3(0.5f, -0.5f, 0.5f); GL.Vertex3(0.5f, 0.5f, 0.5f); GL.Vertex3(-0.5f, 0.5f, 0.5f); // 背面(Z-) GL.Color3(_colorDict["后"]); GL.Vertex3(-0.5f, -0.5f, -0.5f); GL.Vertex3(-0.5f, 0.5f, -0.5f); GL.Vertex3(0.5f, 0.5f, -0.5f); GL.Vertex3(0.5f, -0.5f, -0.5f); // 顶面(Y+) GL.Color3(_colorDict["上"]); GL.Vertex3(-0.5f, 0.5f, -0.5f); GL.Vertex3(0.5f, 0.5f, -0.5f); GL.Vertex3(0.5f, 0.5f, 0.5f); GL.Vertex3(-0.5f, 0.5f, 0.5f); // 底面(Y-) GL.Color3(_colorDict["下"]); GL.Vertex3(-0.5f, -0.5f, -0.5f); GL.Vertex3(0.5f, -0.5f, -0.5f); GL.Vertex3(0.5f, -0.5f, 0.5f); GL.Vertex3(-0.5f, -0.5f, 0.5f); // 右面(X+) GL.Color3(_colorDict["右"]); GL.Vertex3(0.5f, -0.5f, -0.5f); GL.Vertex3(0.5f, 0.5f, -0.5f); GL.Vertex3(0.5f, 0.5f, 0.5f); GL.Vertex3(0.5f, -0.5f, 0.5f); // 左面(X-) GL.Color3(_colorDict["左"]); GL.Vertex3(-0.5f, -0.5f, -0.5f); GL.Vertex3(-0.5f, 0.5f, -0.5f); GL.Vertex3(-0.5f, 0.5f, 0.5f); GL.Vertex3(-0.5f, -0.5f, 0.5f); GL.End(); } private bool _isDragging = false;//是否增在拖动 private bool _isRotating = false;//是否正在旋转 private Point _lastMousePos;//最近一次鼠标位置 /// /// 处理鼠标滚轮 /// public void HandleMouseWheel(MouseEventArgs e) { this.Zoom *= e.Delta > 0 ? 0.9f : 1.1f; UpdateModelMatrix(); UpdateViewMatrix(); UpdateProjectionMatrix(); this.glControl1.Invalidate(); } /// /// 处理鼠标按下 /// public void HandleMouseDown(MouseEventArgs e) { _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) { _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) { 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; UpdateModelMatrix(); UpdateViewMatrix(); UpdateProjectionMatrix(); } else if (_isDragging) { float xratio = _pw / this.glControl1.Width; float yratio = _ph / this.glControl1.Height; _translationX += dx * xratio; _translationY -= dy * yratio; UpdateModelMatrix(); UpdateViewMatrix(); UpdateProjectionMatrix(); } _lastMousePos = e.Location; this.glControl1.Invalidate(); } } }