このセクションでは主にグラフィックス描画の原理、紹介、コードのデモについて説明します。
-
原理紹介
まず、OpenGL のグラフィックス レンダリングについて話しましょう。実際、すべてのグラフィックスは多くの小さなグラフィックスによって接続されています。画像のピクセルとして理解することができますが、カラー画像はさまざまな色の多数のピクセルで構成されています。カラフルなグラフィック デザインを実現するには、2 つの点が線を形成し、3 つの点が平面を形成する原理と、多面形成の原理を理解する必要があります。ここでは、簡単なプログラムを通じて、点から線、線から面、および面から形状へのプロセスを示します。
ここではよく使われる機能と描画原理を紹介します。
1. 座標原点変換:Translate(float x, float y, float z)、座標軸変換としても理解できます。デフォルトで設定されている画角と世界座標は同じ原点にあります。たとえば、電話を見て、電話が原点であり、画角もデフォルトで原点であると仮定すると、ドリルする必要があります。私たちの頭は電話に向かっています。そうです、私たちは電話をそのように見るのではなく、基本的に電話を引き離し、快適な角度で操作します。これがアイテムの変形です。したがって、グラフを描画する前に適切な座標軸位置を設定するのが最善であり、視点変換の設定値も考慮する必要があります。入力グラフィックスの座標は依然として原点に従って入力されており、Translate の影響を受けません。影響を受けるのは全体的な座標軸の変換のみであり、その値を変更する必要はありません。もちろん、グラフィックスのすべての座標値を直接変更することもできますが、面倒なので不要で、初期化された視野角変換値を直接変更することも可能です。
2. 点、線、および平面を描画する方法: Begin (BeginMode モード) で描画モード タイプのグラフィックスを開始し、頂点 (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. 色の割り当て: カラー(フロート赤、フロート緑、フロート青)
グラフの各頂点の前に設定した色を追加すると、グラフの描画オプションがより多様になると同時に、異なる頂点間で異なる色を使用すると、グラデーションの色の違いが生成され、非常に美しくなります。
-
デモコード
それでは、まず座標系の変更を追加しましょう。選択すると線が描画され、次に三角形、最後にピラミッドが描画されます。これは、点から線、線から面、面から形状へのプロセスであり、すべてのグラフィックスはこのように結合され、接続されます。
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. 次に、面を描画し、その面(三角形)に少し色の変更 #領域ライン を追加します。
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. 次に、ピラミッドを描き、それを区切る 4 番目の点の色として白を選択します。ピラミッドも 4 つの面で構成されているため、さらに 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
インターフェースが変わっていないことがわかります。さて、4 番目の頂点がどこにあるのか見てみましょう。座標 (0, 0, -2)、他の 4 点に対して、その位置は内側になります。なぜなら、現時点での私たちの視点は前向きだからです。さて、ところで、視野角について話さなければなりませんが、これは初期化で設定される視点変換によって設定されます。
// 現在の行列モードを設定し、後続の行列演算を射影
行列に適用します。 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軸の場合、法線は画面外側に対して垂直になります)
話に戻りますが、4 番目の頂点はすでに描画されている三角形の内側にあるため見えません。次の図に示すように、効果を確認するために少し回転させてみましょう。
すべてのコードを添付します。このコードは「.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;
}
}
}
効果: