阅读该篇以前,推荐优先阅读—OpenTK—空间中单个三维点的绘制,之前做过的解释这里不再赘述。这里直接上代码并展示最终的效果。

  1. 创建主程序中的类
using OpenTK.Mathematics;
using OpenTK.Windowing.Desktop;

namespace OpenTK_SelfMadeBasis
{
class Program
{
static void Main(string[] args)
{
NativeWindowSettings nativeWindowSettings = new NativeWindowSettings()
{
Size = new Vector2i(800, 600),
Title = "Draw Single Line",
};
using (Window window = new Window(GameWindowSettings.Default, nativeWindowSettings))
{
window.Run();
}
}
}
}
  1. 构建主程序中调用的​​GUI​​窗口界面的类
using OpenTK_SelfMadeBasis.Common;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Windowing.Desktop;

namespace OpenTK_SelfMadeBasis
{
public class Window : GameWindow
{
// 设定线段的两个端点坐标
private readonly float[] _vertices =
{
-0.5f, -0.5f, 0.0f, // 左边端点
0.5f, -0.5f, 0.0f, // 右边端点
};

private int _vertexBufferObject;

private int _vertexArrayObject;

private Shader _shader;

public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
{
}

protected override void OnLoad()
{
GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);
_vertexBufferObject = GL.GenBuffer();

GL.BindBuffer(BufferTarget.ArrayBuffer, _vertexBufferObject);

GL.BufferData(BufferTarget.ArrayBuffer, _vertices.Length * sizeof(float), _vertices, BufferUsageHint.StaticDraw);

_vertexArrayObject = GL.GenVertexArray();
GL.BindVertexArray(_vertexArrayObject);

GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
// GL.VertexAttribPointer()函数主要是将端点数据传递给端点着色器使用。
// 第一个参数是输入着色器中的变量的位置,因为数据位于起始处,因此这里第一个参数应该为0,对应layout=0
// 第二个参数是有多少个元素将被传递给变量,在这个例子中,每个端点具有三个float值,故为3
// 第三个参数是元素集合的类型,这里为float
// 第四个参数是数据是否应该被转换到NDC坐标系,这里不需要,因为我们已经设定在NDC坐标系下
// 步幅,每个端点三个float值,因此为3 * sizeof(float)
// 偏移量,应该跳过多少值去获取第一个值,这里我们的端点赋值为连续的,并且从最开始计算,故为0

GL.EnableVertexAttribArray(0);

_shader = new Shader("Shaders/shader.vert", "Shaders/shader.frag");

_shader.Use();

base.OnLoad();
}

protected override void OnRenderFrame(FrameEventArgs e)
{
GL.Clear(ClearBufferMask.ColorBufferBit);

_shader.Use();

GL.BindVertexArray(_vertexArrayObject);
// 因为我们要画线,所以这里的PrimitiveType要使用Lines类型
GL.DrawArrays(PrimitiveType.Lines, 0, 2);

SwapBuffers();

base.OnRenderFrame(e);
}

protected override void OnUpdateFrame(FrameEventArgs e)
{
var input = KeyboardState;

if (input.IsKeyDown(Keys.Escape))
{
Close();
}

base.OnUpdateFrame(e);
}

protected override void OnResize(ResizeEventArgs e)
{
GL.Viewport(0, 0, Size.X, Size.Y);
base.OnResize(e);
}

protected override void OnUnload()
{
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
GL.BindVertexArray(0);
GL.UseProgram(0);

// Delete all the resources.
GL.DeleteBuffer(_vertexBufferObject);
GL.DeleteVertexArray(_vertexArrayObject);

GL.DeleteProgram(_shader.Handle);
base.OnUnload();
}
}
}

在之前的示例中我们并未详细介绍过​​GL.DrawArrays(PrimitiveType.Lines, 0, 2)​​函数的参数,这里做一下补充。

​GL.DrawArrays()​​函数调用方法:

DrawArrays(PrimitiveType mode, int first, int count);

各个参数的意义:。
​​​PrimitiveType mode​​:说明要渲染图元的类型,该参数所有可选的类型如下:

PrimitiveType可用参数

对应OpenGL

Points

GL_LINES = 0x0000

Lines

GL_LINES = 0x0001

LineLoop

GL_LINES = 0x0002

LineStrip

GL_LINES = 0x0003

Triangles

GL_LINES = 0x0004

TriangleStrip

GL_LINES = 0x0005

TriangleFan

GL_LINES = 0x0006

Quads

GL_LINES = 0x0007

QuadsExt

GL_LINES = 0x0007

LinesAdjacency

GL_LINES = 0x000A

LinesAdjacencyArb

GL_LINES = 0x000A

LinesAdjacencyExt

GL_LINES = 0x000A

LineStripAdjacency

GL_LINES = 0x000B

LineStripAdjacencyArb

GL_LINES = 0x000B

LineStripAdjacencyExt

GL_LINES = 0x000B

TrianglesAdjacency

GL_LINES = 0x000C

TrianglesAdjacencyArb

GL_LINES = 0x000C

TrianglesAdjacencyExt

GL_LINES = 0x000C

TriangleStripAdjacency

GL_LINES = 0x000D

TriangleStripAdjacencyArb

GL_LINES = 0x000D

TriangleStripAdjacencyExt

GL_LINES = 0x000D

Patches

GL_LINES = 0x000E

PatchesExt

GL_LINES = 0x000E

我们使用时应该使用第一列中的参数,这里的第二列表示我们使用参数对应的​​OpenGL​​中的参数,仅仅供对比理解使用。

​int first​​​:说明数组开始的索引值,本例中我们使用的元素从第一个开始,因此该值应该被设置为​​0​​​。
​​​int count​​​:说明需要被渲染的索引的个数,本例中我们有两个端点需要被渲染,因此该值应该被设置为​​2​​。

  1. 着色器类
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;


// 注意这里的K是大写的
// 一定要注意这里改变了namespace的名字并且还要在project->properties中改变namespace的名字才行,否则无效
namespace OpenTK_SelfMadeBasis.Common
{
public class Shader
{
public readonly int Handle;

private readonly Dictionary<string, int> _uniformLocations;

public Shader(string vertPath, string fragPath)
{
var shaderSource = File.ReadAllText(vertPath);

var vertexShader = GL.CreateShader(ShaderType.VertexShader);

GL.ShaderSource(vertexShader, shaderSource);

CompileShader(vertexShader);

shaderSource = File.ReadAllText(fragPath);
var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragmentShader, shaderSource);
CompileShader(fragmentShader);

Handle = GL.CreateProgram();

GL.AttachShader(Handle, vertexShader);
GL.AttachShader(Handle, fragmentShader);

LinkProgram(Handle);

GL.DetachShader(Handle, vertexShader);
GL.DetachShader(Handle, fragmentShader);
GL.DeleteShader(fragmentShader);
GL.DeleteShader(vertexShader);

GL.GetProgram(Handle, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms);

_uniformLocations = new Dictionary<string, int>();

for (var i = 0; i < numberOfUniforms; i++)
{
var key = GL.GetActiveUniform(Handle, i, out _, out _);

var location = GL.GetUniformLocation(Handle, key);

_uniformLocations.Add(key, location);
}
}

private static void CompileShader(int shader)
{
GL.CompileShader(shader);

GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
if (code != (int)All.True)
{
var infoLog = GL.GetShaderInfoLog(shader);
throw new Exception($"Error occurred whilst compiling Shader({shader}).\n\n{infoLog}");
}
}

private static void LinkProgram(int program)
{
GL.LinkProgram(program);

GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code);
if (code != (int)All.True)
{
throw new Exception($"Error occurred whilst linking Program({program})");
}
}

public void Use()
{
GL.UseProgram(Handle);
}

public int GetAttribLocation(string attribName)
{
return GL.GetAttribLocation(Handle, attribName);
}

public void SetInt(string name, int data)
{
GL.UseProgram(Handle);
GL.Uniform1(_uniformLocations[name], data);
}

public void SetFloat(string name, float data)
{
GL.UseProgram(Handle);
GL.Uniform1(_uniformLocations[name], data);
}

public void SetMatrix4(string name, Matrix4 data)
{
GL.UseProgram(Handle);
GL.UniformMatrix4(_uniformLocations[name], true, ref data);
}

public void SetVector3(string name, Vector3 data)
{
GL.UseProgram(Handle);
GL.Uniform3(_uniformLocations[name], data);
}
}
}
  1. 端点着色器
#version 330 core

layout(location = 0) in vec3 aPosition;

void main(void)
{
gl_Position = vec4(aPosition, 1.0);
}
  1. 片段着色器
#version 330

out vec4 outputColor;

void main()
{
outputColor = vec4(1.0, 1.0, 0.0, 1.0);
}

运行程序可以得到下面的结果:

OpenTK---空间中单条线段的绘制_opengl

码字不易,如果大家觉得有用,请高抬贵手给一个赞让我上推荐让更多的人看到吧~