之前我们已经提及了如何在空间中绘制单条线段,这里,我们将会说到如何在空间中绘制三条线段,使它们相互组合看起来像是一个三角形。注意,这里实际上我们所画的并不是一个三角形,因为三角形通常要求三条线段所围成的部分被填充。
- 创建主程序中的类
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 Three Lines",
};
using (Window window = new Window(GameWindowSettings.Default, nativeWindowSettings))
{
window.Run();
}
}
}
}
截至到这篇为止,我们可以看到,无论我们想要在空间中画什么样的图形,主程序中的类都拥有相同的形式。唯一的差异是我们重新定义了Title
参数的值,现在是Draw Three Lines
。
- 构建主程序中调用的
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,
0.0f, 0.5f, 0.0f,
};
// uint类型表示仅仅为单个的一个数,常作为placeholder(位置占用符)用
// 三个点,通过第0个点和第2个点绘制第1条线段,通过第0个点和第2个点绘制第2条线段,通过第1个点和第2个点绘制第3条线段
private readonly uint[] _indices =
{
0, 1,
0, 2,
1, 2,
};
private int _vertexBufferObject;
private int _vertexArrayObject;
private Shader _shader;
private int _elementBufferObject; // EBO,主要用于控制绘制三条线段的顺序
public Window(GameWindowSettings gameWindowSettings, NativeWindowSettings nativeWindowSettings)
: base(gameWindowSettings, nativeWindowSettings)
{
}
protected override void onl oad()
{
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.EnableVertexAttribArray(0);
_elementBufferObject = GL.GenBuffer(); // VBO
GL.BindBuffer(BufferTarget.ElementArrayBuffer, _elementBufferObject);
GL.BufferData(BufferTarget.ElementArrayBuffer, _indices.Length * sizeof(uint), _indices, BufferUsageHint.StaticDraw);
_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);
GL.DrawElements(PrimitiveType.Lines, _indices.Length, DrawElementsType.UnsignedInt, 0);
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);
GL.DeleteBuffer(_vertexBufferObject);
GL.DeleteVertexArray(_vertexArrayObject);
GL.DeleteProgram(_shader.Handle);
base.OnUnload();
}
}
}
- 着色器类
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
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);
}
}
}
仔细对比不难发现,我们的着色器类也与前面提及的着色器类别无二致。
- 端点着色器
#version 330 core
layout(location = 0) in vec3 aPosition;
void main(void)
{
gl_Position = vec4(aPosition, 1.0);
}
- 片段着色器
#version 330
out vec4 outputColor;
void main()
{
outputColor = vec4(1.0, 1.0, 0.0, 1.0);
}
仔细对比不难发现,我们GLSL
写的端点和片段着色器与之前绘制单条直线时所使用的着色器写法一致。
运行程序可以得到下面的结果:
码字不易,如果大家觉得有用,请高抬贵手给一个赞让我上推荐让更多的人看到吧~