.Net平台下OpenGL绘制图形(2)(VS2019,Winform,C#)

news/2024/2/28 5:12:45

本节主要讲诉图形绘制的原理,使用介绍和代码演示。

  • 原理介绍

  我们先来讲讲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);	// 设置坐标,距离屏幕距离为6gl.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;}}
}

效果:

 

 

 


http://www.ppmy.cn/news/66627.html

相关文章

【Linux】进程与文件系统(上)

由于这部分的知识很多很多&#xff0c;分成两回 目录 1.文件描述符 文件描述符 1.文件描述符 首先我们看一下几个小问题 1.你真的理解文件原理和操作了吗&#xff1f; 这不是语言的问题&#xff0c;而是操作系统的问题 2.是不是只有C/C有文件操作&#xff1f; 其他语…

uniapp 微信小程序订阅(一次性订阅消息)

首先我们需要了解微信小程序的一些基本的&#xff0c;才能知道我们要做什么&#xff1a; 微信小程序消息订阅只有两种形式可以召唤出来&#xff1a; 1、用户手动点击按钮 2、支付回调唤起 一次调用最多可订阅3条消息 小程序弹出后&#xff0c;可点击的情况 1、单纯点击取消/确…

Linux介绍及环境搭建

文章目录 &#x1f3ac;1.Linux背景&#x1f4bb;1.1 计算机的发展&#x1f4bb;1.2 操作系统的故事&#x1f4bb;1.3 Linux操作系统&#x1f4bb;1.4 Linux的应用场景&#x1f4bb;1.5 Linux版本 &#x1f50c;2. Linux环境&#x1f4be;2.1 环境选择&#x1f4be;2.2 云服务器…

RHCSA 作业一

[rootserver ~]# mkdir /opt/tmp #在/opt目录下创建一个目录tmp [rootserver ~]# ls /opt tmp [rootserver ~]# touch /opt/tmp/a.txt #在tmp目录下新建一个文件a.txt [rootserver ~]# ls /opt/tmp a.txt [rootserver ~]# cd /opt/tmp #进入tmp目录下 [rootserver tmp]# …

uvm寄存器模型

一、基础知识 前门访问与后门访问是两种寄存器的访问方式。 所谓前门访问, 指的是通过模拟cpu在总线上发出读指令, 进行读写操作。 在这个过程中, 仿真时间( $time函数得到的时间) 是一直往前走的。而后门访问是与前门访问相对的概念。 它并不通过总线进行读写操作, 而是…

ChatGPT入门到高级【第五章】

第一章&#xff1a;Chatgpt的起源和发展 1.1 人工智能和Chatbot的概念 1.2 Chatbot的历史发展 1.3 机器学习技术在Chatbot中的应用 1.4 Chatgpt的诞生和发展 第二章&#xff1a;Chatgpt的技术原理 2.1 自然语言处理技术 2.2 深度学习技术 2.3 Transformer模型 2.4 GPT模型 第…

设计模式MVC、MVP、MVVM

MVC、MVP和MVVM是什么&#xff1f; MVC&#xff1a;Model-View-Controller&#xff0c;是一种分层解偶的框架&#xff0c;Model层提供本地数据和网络请求&#xff0c;View层处理视图&#xff0c;Controller处理逻辑&#xff0c;存在问题是Controller层和View层的划分不明显&am…

hnust 湖南科技大学 2023 软件测试技术 期中考试 复习资料

前言 写的比较匆忙&#xff0c;重点也不明确&#xff0c;没什么参考价值致谢&#xff1a;ly&#xff0c;zxq重点来源&#xff1a;信安※&#xff1a;补充内容★&#xff1a;重点✦&#xff1a;个人推测考点考试范围&#xff1a;1-9章获取最新版本 题型 判断&#xff1a;10简…

银行面试中:面试官反感的求职者有哪些?(上)

银行面试过程中&#xff0c;很多考生往往会因为各种不好的表现而痛失心仪的岗位&#xff0c;小编给大家总结了面试中常见的一些问题&#xff0c;希望大家可以远离这些不好的表现&#xff0c;争取得到考官的青睐。我从如信银行考试中心了解到的是&#xff1a; 第一、自带负能量 …

SGA和PGA调整

专业词汇解释 (1) SGA&#xff1a;System Global Area是Oracle Instance的基本组成部分&#xff0c;在实例启动时分配;系统全局域SGA主要由三部分构成&#xff1a;共享池、数据缓冲区、日志缓冲区。 (2)共享池&#xff1a;Shared Pool用于缓存最近被执行的SQL语句和最近被使用…

去阿里面试,面试前20分钟突然要求候选人展示过去的工作方案,候选人拒绝后,竟被取消面试!...

离职时&#xff0c;你会把自己的工作成果拷贝下来留档吗&#xff1f; 一位网友说&#xff1a; 面试阿里&#xff0c;面试前20分钟&#xff0c;面试官突然要求他展示过去的工作成果&#xff0c;因为之前是用公司电脑&#xff0c;离职时把电脑交上去了&#xff0c;没有任何留档&a…

linux系统函数的运用

函数 函数详解函数的作用函数的定义函数的返回值函数的作用范围函数传参函数递归函数库 函数详解 函数的作用 在编写shell脚本的时候&#xff0c;经常会发现在多个地方使用了同一段代码&#xff0c;如果只是一小段代码&#xff0c;一般也无关紧要&#xff0c;但是要在脚本中多…

RabbitMQ消息队列实战(5)—— 发后即忘和远程RPC数据传输模型

本文我们学习下使用RabbitMQ实现的几种数据发送的模型——发后即忘模型和远程RPC调用。二者实际上是从业务的角度定义的一个RabbitMQ的使用模型。发后即忘模型&#xff0c;强调发送时不太关心消息接收者的执行结果&#xff0c;仅仅是为了发送信息。而远程RPC调用模型强调&#…

Kali-linux应用更新和配置额外安全工具

本节将介绍更新Kali的过程和配置一些额外的工具。这些工具在后面的章节中将是有用的。Kali软件包不断地更新和发布之间&#xff0c;用户很快发现一套新的工具比最初在DVD ROM上下载的软件包更有用。本节将通过更新安装的方法&#xff0c;获取Nessus的一个激活码。最后安装Squid…

KingbaseES 复制冲突之锁类型冲突

背景 昨天遇到客户现场的一个有关复制冲突的问题 备库报错&#xff1a;ERROR: canceling statement due to conflict with recovery&#xff0c;user was holding a relation lock for too long 现场情景是备库执行逻辑备份过程中出现的报错&#xff0c;逻辑备份相当于备库查询…

yolov5 用自己的数据集进行训练

在训练之前先要按照一定目录格式准备数据&#xff1a; VOC标签格式转yolo格式并划分训练集和测试集_爱钓鱼的歪猴的博客-CSDN博客 目录 1、修改数据配置文件 2、修改模型配置文件 3、训练 1、修改数据配置文件 coco.yaml 拷贝data/scripts/coco.yaml文件&#xff0c; pa…

k8s二进制搭建|ETCD + Flannel | 单节点部署 | 多节点的部署|dashbord的部署

k8s二进制搭建|ETCD Flannel | 单节点部署 | 多节点的部署|dashbord的部署 二进制搭建 Kubernetes v1.201 初始化环境2 部署 docker引擎3 在mster 192.168.10.10上操作4 在 node01 192.168.10.20节点上操作5 在 node02 192.168.10.30节点上操作6 检查etcd群集状态7 部署 Maste…

Java设计模式(二十二)策略模式

一、概述 策略模式是一种行为型设计模式&#xff0c;它允许在运行时选择算法的行为。策略模式通过将算法封装成独立的策略类&#xff0c;使得它们可以相互替换&#xff0c;而不影响使用算法的客户端。这样可以使客户端代码与具体算法的实现细节解耦&#xff0c;提高了代码的可…

03FPGA—led灯的显示(入门)

学习fpga也有段时间了&#xff0c;但后台有几个朋友问我能不能分享一点简单入门例子&#xff0c;于是我打算发经典的如何控制led的例子,本文主要分享设计流程以及简单的verilog语法。 设计流程主要包括五个步骤模块设计、波形设计、编写rtl代码、仿真验证、上板验证&#xff0c…

r语言tidyverse教程:4 dplyr

文章目录 简介和数据准备行列筛选mutate数据概述集合运算 R语言系列&#xff1a; 编程基础&#x1f48e;循环语句&#x1f48e;向量、矩阵和数组&#x1f48e;列表、数据帧排序函数&#x1f48e;apply系列函数tidyverse&#xff1a;readr&#x1f48e;tibble&#x1f48e;tidy…
最新文章