您现在的位置是:首页 >技术杂谈 >.Net平台下OpenGL绘制图形(2)(VS2019,Winform,C#)网站首页技术杂谈
.Net平台下OpenGL绘制图形(2)(VS2019,Winform,C#)
本节主要讲诉图形绘制的原理,使用介绍和代码演示。
-
原理介绍
我们先来讲讲OpenGL的图形绘制。其实,所有的图形都是由许多个小图形连接而成的。你可以理解为是图片的像素,一张彩图是由很多个色彩不一的像素点组合而成。要实现色彩绚丽的图形设计,你需要理解两点成线,三点成面,多面成形的原理。在这里,我们通过简单的程序演示一下点到线,线到面,面到形的过程。
在此,先介绍一下几个常用的函数和绘制原理。
1. 坐标原点变换:Translate(float x, float y, float z) ,你也可以理解为是坐标轴变换。默认设定的视角和世界坐标都是在同个原点,打个比方:我们看手机,假设手机是原点,我们视角默认也是原点的话,我们脑壳都得钻手机里面。对吧,我们不这么看手机,基本都是把手机拉远,以一种舒服的角度进行操作,这就是在物品的变换了。所以我们,在绘制图形前最好设定合适的坐标轴位置,同时也需要考虑视点变换的设置值。我们输入图形的坐标仍是按照原点来输入的,不会受到Translate的影响;他影响的只是整体的坐标轴变换,他的值是不需要做改变的。当然了,你也可以直接全部修改图形的坐标值,不过这样就太麻烦了,没必要;直接修改初始化的视角变换值也是可以的。
2. 绘制点,线,面的方式:以Begin(BeginMode mode)为首开始绘制mode类型的图形,Vertex(float x, float y, float z)为图形顶点,依次连接各个顶点形成图形,以End()结束绘制。理论上来说,只要你给出图形的所有顶点,牛鬼蛇神都给你搞出来,一点问题也没有(除了可能会卡之外)。以画线为例:
gl.Begin(OpenGL.GL_LINES);
gl.Vertex(-1.0f, 0.0f, 0.0f);
gl.Vertex(1.0f, 0.0f, 0.0f);
gl.End();
3. 赋予颜色:Color(float red, float green, float blue)
在每个图形顶点前加上设定颜色,图形的绘制选择就更加多样;同时,不同顶点间采用不同的颜色会产生渐变色差,非常好看。
-
演示代码
我们先添加一个坐标系变化,然后。选择绘制一条线,接着绘制一个三角形,最后绘制一个角锥。这就是由点到线,线到面,面到形的过程,所有的图形都是这样组合衔接而来的。
1.首先,画一条线,颜色设定为白色(背景是黑色)
#region 点到线
gl.Begin(OpenGL.GL_LINES);
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(-2.0f, 0.0f, 0.0f);//左顶点
gl.Vertex(2.0f, 2.0f, 0.0f);//右顶点
gl.End();
#endregion
2.接着,来画个面,再稍微加点颜色变化#region 线成面(三角形)
gl.Begin(OpenGL.GL_TRIANGLES);//第一个面
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(0.0f, 1f, 0.0f);//顶点
gl.Color(0.0f, 1.0f, 0.0f);
gl.Vertex(-1.0f, -1.0f, 0.0f);//左顶点
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 0.0f);//右顶点
gl.End();
#endregion
3.接着,来画个角锥,第四个点颜色选择白色区分开。角锥也就是由四个面组合而成的,所以我们再加上三个面(注意每个点坐标和对应颜色要相同)
#region 面组合成体
gl.Begin(OpenGL.GL_TRIANGLES);//第二个面
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(0.0f, 0.0f, -2.0f);//第四个点
gl.Color(0.0f, 1.0f, 0.0f);
gl.Vertex(-1.0f, -1.0f, 0.0f);//左顶点
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 0.0f);//右顶点
gl.End();
gl.Begin(OpenGL.GL_TRIANGLES);//第三个面
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(0.0f, 0.0f, -2.0f);//第四个点
gl.Color(0.0f, 1.0f, 0.0f);
gl.Vertex(-1.0f, -1.0f, 0.0f);//左顶点
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(0.0f, 1f, 0.0f);//顶点
gl.End();
gl.Begin(OpenGL.GL_TRIANGLES);//第四个面
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(0.0f, 0.0f, -2.0f);//第四个点
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 0.0f);//右顶点
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(0.0f, 1f, 0.0f);//顶点
gl.End();
#endregion
我们会发现,界面没什么变化。嗯,大伙看看我们的第四个顶点在哪里。坐标(0,0,-2),相对于其他四个点,他的位置是靠里面的。因为我们此时的视角是在正值的。额,对了,还得说说视角的事,视角受到初始化设置的视点变换设置。
// 设置当前矩阵模式,对投影矩阵应用随后的矩阵操作
gl.MatrixMode(OpenGL.GL_PROJECTION);
// 创建透视投影变换
gl.Perspective(30.0f, (double)Width / (double)Height, 5, 100.0);
// 视点变换
gl.LookAt(0, 5, 0, 0, 0, 0, 0, 1, 0);
//此时的视角在这里,可以理解为是坐标点(0,5,0)往空间原点处看
程序的前端添加了Translate(0.0f, 0.0f, -5.0f) ,相当于把绘制的图形往屏幕内移动。这样会有利于我们查看图形。(Z轴的话法线是垂直屏幕往外的)
说回正题,第四个顶点是在已经画好的三角形的里面,所以我们看不到,那我们就稍微的旋转一下看看效果,如下图所示。
附上全部代码,本代码是在“.Net平台下OpenGL绘制图形(1)(VS2019,Winform,C#)“基础上增加的
using SharpGL;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SharpGLFormsApp1
{
public partial class Form1 : Form
{
private bool drawLine = false;
private bool drawArea = false;
private bool drawVolume = false;
private bool flagRotateX = false;
private bool flagRotateY = false;
private bool flagRotateZ = false;
private float rotation_X = 0.0f;
private float rotation_Y = 0.0f;
private float rotation_Z = 0.0f;
/// <summary>
/// 默认绘画模式为线条
/// </summary>
private uint _model = OpenGL.GL_LINE_LOOP;
/// <summary>
/// X轴坐标
/// </summary>
private float _x = 0;
/// <summary>
/// Y轴坐标
/// </summary>
private float _y = 0;
/// <summary>
/// Z轴坐标
/// </summary>
private float _z = 0;
public Form1()
{
InitializeComponent();
}
/// <summary>
/// 复位事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
_x = _y = _z = 0;
tbX.Value = tbY.Value = tbZ.Value = Convert.ToInt32(_x);
label1.Text = "X轴" ;
label2.Text = "Y轴";
label3.Text = "Z轴";
}
/// <summary>
/// 线条选择事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void rbline_CheckedChanged(object sender, EventArgs e)
{
_model = OpenGL.GL_LINE_LOOP;
}
/// <summary>
/// 球面事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void rbfull_CheckedChanged(object sender, EventArgs e)
{
_model = OpenGL.GL_QUADS;
}
/// <summary>
/// 控件绘图事件
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
private void openGLControl1_GDIDraw(object sender, RenderEventArgs args)
{
// 创建一个GL对象
OpenGL gl = this.openGLControl1.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); // 清空屏幕
gl.LoadIdentity(); // 重置
gl.Translate(0.0f, 0.0f, -6.0f); // 设置坐标,距离屏幕距离为6
gl.Rotate(_x, 1.0f, 0.0f, 0.0f); // 绕X轴旋转
gl.Rotate(_y, 0.0f, 1.0f, 0.0f); // 绕Y轴旋转
gl.Rotate(_z, 0.0f, 0.0f, 1.0f); // 绕Z轴旋转
gl.Begin(_model); // 绘制立方体
gl.Color(0.0f, 1.0f, 0.0f); // 设置颜色
//绘制其中一个面
gl.Vertex(1.0f, 1.0f, -1.0f);
gl.Vertex(-1.0f, 1.0f, -1.0f);
gl.Vertex(-1.0f, 1.0f, 1.0f);
gl.Vertex(1.0f, 1.0f, 1.0f);
//如下类同
gl.Color(1.0f, 0.5f, 0.0f);
gl.Vertex(1.0f, -1.0f, 1.0f);
gl.Vertex(-1.0f, -1.0f, 1.0f);
gl.Vertex(-1.0f, -1.0f, -1.0f);
gl.Vertex(1.0f, -1.0f, -1.0f);
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(1.0f, 1.0f, 1.0f);
gl.Vertex(-1.0f, 1.0f, 1.0f);
gl.Vertex(-1.0f, -1.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 1.0f);
gl.Color(1.0f, 1.0f, 0.0f);
gl.Vertex(1.0f, -1.0f, -1.0f);
gl.Vertex(-1.0f, -1.0f, -1.0f);
gl.Vertex(-1.0f, 1.0f, -1.0f);
gl.Vertex(1.0f, 1.0f, -1.0f);
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(-1.0f, 1.0f, 1.0f);
gl.Vertex(-1.0f, 1.0f, -1.0f);
gl.Vertex(-1.0f, -1.0f, -1.0f);
gl.Vertex(-1.0f, -1.0f, 1.0f);
gl.Color(1.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, 1.0f, -1.0f);
gl.Vertex(1.0f, 1.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, -1.0f);
gl.End(); // 结束绘制
}
/// <summary>
/// X轴拖动事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tbX_Scroll(object sender, EventArgs e)
{
int x = tbX.Value;
_x = x;
label1.Text = "X:" + x;
}
/// <summary>
/// Y轴拖动事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tbY_Scroll(object sender, EventArgs e)
{
int y = tbY.Value;
_y = y;
label2.Text = "Y:" + y;
}
/// <summary>
///Z轴拖动事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void tbZ_Scroll(object sender, EventArgs e)
{
int z = tbZ.Value;
_z = z;
label3.Text = "Z:" + z;
}
private void openGLControl2_GDIDraw(object sender, RenderEventArgs args)
{
SharpGL.OpenGL gl = this.openGLControl2.OpenGL;
//清除深度缓存
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
//重置当前指定的矩阵为单位矩阵,将当前的用户坐标系的原点移到了屏幕中心
gl.LoadIdentity();
//坐标轴变换位置到(0.0f, 0.0f, -5.0f),这样我们的坐标轴就相当于往屏幕内走5个单位
gl.Translate(0.0f, 0.0f, -5.0f);
if (flagRotateX)
{
rotation_X += 1f;
gl.Rotate(rotation_X, 1.0f, 0.0f, 0.0f);//rotationX:角度
}
if (flagRotateY)
{
rotation_Y += 1f;
gl.Rotate(rotation_Y, 0.0f, 1.0f, 0.0f);//rotationY:角度
}
if (flagRotateZ)
{
rotation_Z += 1f;
gl.Rotate(rotation_Z, 0.0f, 0.0f, 1.0f);//rotationZ:角度
}
if (drawLine)
{
#region 点到线
gl.Begin(OpenGL.GL_LINES);
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(-2.0f, -1.0f, 0.0f);//左顶点
gl.Vertex(2.0f, 2.0f, 0.0f);//右顶点
gl.End();
#endregion
}
if (drawArea)
{
#region 线成面(三角形)
gl.Begin(OpenGL.GL_TRIANGLES);//第一个面
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(0.0f, 1f, 0.0f);//顶点
gl.Color(0.0f, 1.0f, 0.0f);
gl.Vertex(-1.0f, -1.0f, 0.0f);//左顶点
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 0.0f);//右顶点
gl.End();
#endregion
}
if (drawVolume)
{
#region 面组合成体
gl.Begin(OpenGL.GL_TRIANGLES);//第二个面
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(0.0f, 0.0f, -2.0f);//第四个点
gl.Color(0.0f, 1.0f, 0.0f);
gl.Vertex(-1.0f, -1.0f, 0.0f);//左顶点
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 0.0f);//右顶点
gl.End();
gl.Begin(OpenGL.GL_TRIANGLES);//第三个面
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(0.0f, 0.0f, -2.0f);//第四个点
gl.Color(0.0f, 1.0f, 0.0f);
gl.Vertex(-1.0f, -1.0f, 0.0f);//左顶点
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(0.0f, 1f, 0.0f);//顶点
gl.End();
gl.Begin(OpenGL.GL_TRIANGLES);//第四个面
gl.Color(1.0f, 1.0f, 1.0f);
gl.Vertex(0.0f, 0.0f, -2.0f);//第四个点
gl.Color(0.0f, 0.0f, 1.0f);
gl.Vertex(1.0f, -1.0f, 0.0f);//右顶点
gl.Color(1.0f, 0.0f, 0.0f);
gl.Vertex(0.0f, 1f, 0.0f);//顶点
gl.End();
#endregion
}
gl.Flush(); //强制刷新
}
private void Form1_Load(object sender, EventArgs e)
{
OpenGL g2 = openGLControl2.OpenGL;
g2.ClearColor(0, 0, 0, 0);
}
private void openGLControl2_Resize(object sender, EventArgs e)
{
OpenGL gl = openGLControl2.OpenGL;
// 设置当前矩阵模式,对投影矩阵应用随后的矩阵操作
gl.MatrixMode(OpenGL.GL_PROJECTION);
// 重置当前指定的矩阵为单位矩阵,将当前的用户坐标系的原点移到了屏幕中心
gl.LoadIdentity();
// 创建透视投影变换
gl.Perspective(30.0f, (double)Width / (double)Height, 5, 100.0);
// 视点变换
gl.LookAt(0, 5, 0, 0, 0, 0, 0, 1, 0);
// 设置当前矩阵为模型视图矩阵
gl.MatrixMode(OpenGL.GL_MODELVIEW);
}
private void ckline_CheckedChanged(object sender, EventArgs e)
{
drawLine = this.ckline.Checked;
}
private void ckarea_CheckedChanged(object sender, EventArgs e)
{
drawArea = this.ckarea.Checked;
}
private void ckvol_CheckedChanged(object sender, EventArgs e)
{
drawVolume = this.ckvol.Checked;
}
private void ckx_CheckedChanged(object sender, EventArgs e)
{
flagRotateX = this.ckx.Checked;
}
private void cky_CheckedChanged(object sender, EventArgs e)
{
flagRotateY = this.cky.Checked;
}
private void ckz_CheckedChanged(object sender, EventArgs e)
{
flagRotateZ = this.ckz.Checked;
}
}
}
效果: