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 交互处理
|
|
/// <summary>
|
/// 处理鼠标滚轮
|
/// </summary>
|
public void HandleMouseWheel(MouseEventArgs e)
|
{
|
if (!this.glControl1.IsHandleCreated)
|
{
|
return;
|
}
|
this.Zoom *= e.Delta > 0 ? 0.9f : 1.1f;
|
UpdateMatrices();
|
this.glControl1.Invalidate();
|
}
|
|
/// <summary>
|
/// 处理鼠标按下
|
/// </summary>
|
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; // 修改光标样式
|
}
|
}
|
|
/// <summary>
|
/// 处理鼠标弹起
|
/// </summary>
|
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();
|
}
|
|
/// <summary>
|
/// 处理鼠标移动
|
/// </summary>
|
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
|
|
|
|
|
}
|
}
|