我正在对我大学的一个游戏引擎进行大修,在处理OpenGL渲染时遇到了一堵墙。我使用OpenTK和GLFW.NET来渲染窗口。(我最近切换到OpenTK,并没有费心去切换到他们的GLFW而不是GLFW.NET,但我可能会在未来这么做)
我将在这里发布的代码是从包含类中取出的,但我也测试过它,但它仍然不能工作。这不是GLFW或链接procAddress的问题,因为我已经设法使用窗口上下文渲染了一个三角形。
在绘制图像的当前试验中,没有GL错误代码。
GLContext.cs
这由GameProcess调用,在创建窗口后使用Run()方法。
private Texture2D tex;
public ShaderProgram shader;
public VertexArrayObject vao;
public VertexBufferObject vbo;
public ElementBufferObject ebo;
Matrix4 viewMatrix;
Matrix4 orthoMatrix;
private readonly uint[] indices =
{
0, 1, 3,
1, 2, 3
};
public override void Run() {
// perform gl setup
GL.Enable(EnableCap.DepthTest);
GL.Enable(EnableCap.Multisample);
//GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);
GL.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
tex = new Texture2D(new Bitmap("Assets/Sprites/Test.png")); // This image exists
tex.Load();
vao = new VertexArrayObject();
vao.Bind();
float[] vertices = new float[]
{
// Position Texture coordinates
0.5f, 0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
-0.5f, 0.5f, 0.0f,
};
float[] texCoords = new float[]
{
1.0f, 1.0f, // top right
1.0f, 0.0f, // bottom right
0.0f, 0.0f, // bottom left
0.0f, 1.0f // top left
};
// Generates Vertex Buffer Object and links it to vertices
VertexBufferObject vbo = new VertexBufferObject(vertices, texCoords);
// Generates Element Buffer Object and links it to indices
ElementBufferObject ebo = new ElementBufferObject(indices);
shader = new ShaderProgram();
shader.Use();
// Links VBO attributes such as coordinates and colors to VAO
vao.LinkAttrib(vbo, shader.GetAttribLocation("aPos"), 3, VertexAttribPointerType.Float, 5 * sizeof(float), 0);
vao.LinkAttrib(vbo, shader.GetAttribLocation("aTex"), 2, VertexAttribPointerType.Float, 5 * sizeof(float), 3 * sizeof(float));
// Unbind all to prevent accidentally modifying them
vao.Unbind();
vbo.Unbind();
ebo.Unbind();
viewMatrix = Matrix4.CreateTranslation(0.0f, 0.0f, -3.0f);
Vector2 widthHeight = GLContext.Instance.windowDimensions.Clone();
widthHeight.x /= GLContext.Instance.ratios.x;
widthHeight.y /= GLContext.Instance.ratios.y;
orthoMatrix = Matrix4.CreateOrthographicOffCenter(0f, widthHeight.x, 0f, widthHeight.y, 0f, 1000f);
Glfw.Time = 0.0D;
do {
if (vsync || Time.time - lastFrameTime > 1000 / targetFrameRate) {
lastFrameTime = Time.time;
frameCount++;
if (Time.time - lastFPSTime > 1000) {
lastFPS = (int)(frameCount / ((Time.time - lastFPSTime) / 1000.0f));
lastFPSTime = Time.time;
frameCount = 0;
}
UpdateMouseInput();
game.Step();
soundSystem.Step();
ResetHitCounters();
// TODO move to different thread
Display();
Time.newFrame();
Glfw.PollEvents();
}
} while (!Glfw.WindowShouldClose(window));
// the process is being terminated
Close();
}
public override void Display() {
// Specify the color of the background
GL.ClearColor(0.07f, 0.13f, 0.17f, 1.0f);
// Clean the back buffer and assign the new color to it
GL.Clear(ClearBufferMask.ColorBufferBit);
//game.Render();
vao.Bind();
tex.Bind();
shader.Use();
//Matrix4 model = transform.matrix;
shader.SetInt("tex0", 0);
shader.SetVector4("color", new Vector4(1f,1f,1f,1f)); // TODO change from Color32 to color clamp (0-1)
//shader.SetMatrix4("model", model);
shader.SetMatrix4("view", viewMatrix);
shader.SetMatrix4("ortho", orthoMatrix);
GL.DrawElements(PrimitiveType.Triangles, indices.Length, DrawElementsType.UnsignedInt, 0);
tex.Unbind();
vao.Unbind();
Console.WriteLine(GL.GetError());
// Swap the back buffer with the front buffer
Glfw.SwapBuffers(window);
}
字符串
Texture2D.cs
using System.Collections;
using System.Drawing;
using System.Drawing.Imaging;
using OpenTK.Graphics.OpenGL4;
#pragma warning disable CA1416 // Validate platform compatibility
public unsafe class Texture2D : AssetInstance
{
private static Texture2D? lastBound = null;
internal Bitmap bitmap;
private uint glTex = 0;
private TextureUnit slot;
public int width
{
get { return bitmap.Width; }
}
public int height
{
get { return bitmap.Height; }
}
private bool _wrap = false;
public bool wrap
{
get { return _wrap; }
set {
// cannot change texture as it was not created
if (glTex == 0) return;
_wrap = value;
// modify tex wrap
Bind();
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS,
value ? new int[] { (int) All.Repeat } : new int[] { (int) All.ClampToEdge });
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT,
value ? new int[] { (int) All.Repeat } : new int[] { (int) All.ClampToEdge });
Unbind();
}
}
public Texture2D(int width, int height) : this(new Bitmap(width, height)) {
if (width == 0 || height == 0) throw new ArgumentException("Width and Height need to be greater than 0!");
}
public Texture2D(Bitmap bitmap) {
if (bitmap == null || bitmap.Width == 0 || bitmap.Height == 0) throw new ArgumentException("Width and Height need to be greater than 0!");
this.bitmap = bitmap;
}
/// <summary>
/// Creates a GL Texture
/// </summary>
internal void CreateTexture(TextureUnit slot = TextureUnit.Texture0) {
if (glTex != 0)
DestroyTexture();
this.slot = slot;
bitmap ??= new Bitmap(64, 64);
fixed (uint* pointer = &glTex)
GL.GenTextures(1, pointer);
GL.ActiveTexture(slot);
GL.BindTexture(TextureTarget.Texture2D, glTex);
if (GameProcess.Main.settings.PixelArt) {
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, new int[] { (int) TextureMinFilter.LinearMipmapNearest });
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, new int[] { (int) TextureMagFilter.Nearest });
}
else {
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, new int[] { (int) TextureMinFilter.LinearMipmapLinear });
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, new int[] { (int) TextureMagFilter.Linear });
}
if (wrap) {
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, new int[] { (int) TextureWrapMode.Repeat });
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, new int[] { (int) TextureWrapMode.Repeat });
}
else {
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, new int[] { (int) TextureWrapMode.ClampToEdge });
GL.TexParameterI(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, new int[] { (int) TextureWrapMode.ClampToEdge });
}
UpdateTexture();
Unbind();
}
/// <summary>
/// Updates the GL Texture with glTexImage2D
/// </summary>
internal void UpdateTexture() {
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
//GL.BindTexture(TextureTarget.Texture2D, glTex);
GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0,
OpenTK.Graphics.OpenGL4.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
bitmap.UnlockBits(data);
}
/// <summary>
/// Destroys the GL Texture
/// </summary>
internal void DestroyTexture() {
fixed (uint* pointer = &glTex)
GL.DeleteTextures(1, pointer);
glTex = 0;
}
internal void Bind() {
if (lastBound == this) return;
lastBound = this;
GL.ActiveTexture(slot);
GL.BindTexture(TextureTarget.Texture2D, glTex);
}
internal void Unbind()
{
if (lastBound != this) return;
GL.BindTexture(TextureTarget.Texture2D, 0);
lastBound = null;
}
internal override void Load() {
CreateTexture();
}
internal override void Unload() {
DestroyTexture();
}
}
#pragma warning restore CA1416
型
VertexArrayObject.cs
using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// https://github.com/VictorGordan/opengl-tutorials/blob/main/YoutubeOpenGL%206%20-%20Textures/VAO.cpp
// translated from c++
/// <summary>
/// Vertex Array Object
/// </summary>
internal class VertexArrayObject
{
private int[] id = new int[1];
public VertexArrayObject() {
GL.GenVertexArrays(1, id);
}
// Links a VBO Attribute such as a position or color to the VAO
public void LinkAttrib(VertexBufferObject vbo, int layout, int numComponents, VertexAttribPointerType type, int stride, nint offset) {
vbo.Bind();
GL.VertexAttribPointer(layout, numComponents, type, false, stride, offset);
GL.EnableVertexAttribArray(layout);
vbo.Unbind();
}
// Binds the VAO
public void Bind() {
GL.BindVertexArray(id[0]);
}
// Unbinds the VAO
public void Unbind() {
GL.BindVertexArray(0);
}
// Deletes the VAO
public void Delete() {
GL.DeleteVertexArrays(1, id);
}
}
型
VertexBufferObject.cs
using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// Vertex Buffer Object
/// </summary>
internal class VertexBufferObject
{
private int[] id = new int[1];
// Constructor that generates a Vertex Buffer Object and links it to vertices
public VertexBufferObject(float[] vertices, float[] texCoords) {
int size = (vertices.Length + texCoords.Length) * sizeof(float); // not sure if it's required to specify sizeof(float)
GL.GenBuffers(1, id);
GL.BindBuffer(BufferTarget.ArrayBuffer, id[0]);
GL.BufferData(BufferTarget.ArrayBuffer, size, IntPtr.Zero, BufferUsageHint.StaticDraw);
GL.BufferSubData(BufferTarget.ArrayBuffer, 0, vertices.Length * sizeof(float), vertices);
GL.BufferSubData(BufferTarget.ArrayBuffer, vertices.Length, texCoords.Length * sizeof(float), texCoords);
}
// Binds the VBO
public void Bind() {
GL.BindBuffer(BufferTarget.ArrayBuffer, id[0]);
}
// Unbinds the VBO
public void Unbind() {
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
}
// Deletes the VBO
public void Delete() {
GL.DeleteBuffers(1, id);
}
}
型
ElementBufferObject.cs
using OpenTK.Graphics.OpenGL4;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
/// <summary>
/// ElementBufferObject
/// </summary>
internal class ElementBufferObject
{
private int[] id = new int[1];
// Constructor that generates a Elements Buffer Object and links it to indices
public ElementBufferObject(uint[] indices) {
int size = indices.Length * sizeof(uint);
GL.GenBuffers(1, id);
GL.BindBuffer(BufferTarget.ElementArrayBuffer, id[0]);
GL.BufferData(BufferTarget.ElementArrayBuffer, size, indices, BufferUsageHint.StaticDraw);
}
// Binds the EBO
public void Bind() {
GL.BindBuffer(BufferTarget.ElementArrayBuffer, id[0]);
}
// Unbinds the EBO
public void Unbind() {
GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
}
// Deletes the EBO
public void Delete() {
GL.DeleteBuffers(1, id);
}
}
型
ShaderProgram.cs(摘自:[https://github.com/opentk/LearnOpenTK/blob/master/Common/Shader.cs])
using OpenTK.Graphics.OpenGL4;
using OpenTK.Mathematics;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
// https://github.com/opentk/LearnOpenTK/blob/master/Common/Shader.cs
public class ShaderProgram
{
public static readonly string[] defaultShaders = new string[] {
"Namespace.To.Shader.Folder.defaultVertex.shader", // these exist in the assembly
"Namespace.To.Shader.Folder.defaultFrag.shader",
};
public static string GetEmbeddedResource(string filename)
{
var assembly = Assembly.GetExecutingAssembly();
using (Stream stream = assembly.GetManifestResourceStream(filename))
using (StreamReader reader = new StreamReader(stream))
{
string result = reader.ReadToEnd();
return result;
}
}
public readonly int Handle;
private readonly Dictionary<string, int> _uniformLocations;
public ShaderProgram() : this(defaultShaders[0], defaultShaders[1], true) { }
// This is how you create a simple shader.
// Shaders are written in GLSL, which is a language very similar to C in its semantics.
// The GLSL source is compiled *at runtime*, so it can optimize itself for the graphics card it's currently being used on.
// A commented example of GLSL can be found in shader.vert.
public ShaderProgram(string vertPath, string fragPath, bool inAssembly = true)
{
// There are several different types of shaders, but the only two you need for basic rendering are the vertex and fragment shaders.
// The vertex shader is responsible for moving around vertices, and uploading that data to the fragment shader.
// The vertex shader won't be too important here, but they'll be more important later.
// The fragment shader is responsible for then converting the vertices to "fragments", which represent all the data OpenGL needs to draw a pixel.
// The fragment shader is what we'll be using the most here.
// Load vertex shader and compile
string shaderSource;
if (inAssembly)
shaderSource = GetEmbeddedResource(vertPath);
else
shaderSource = File.ReadAllText(vertPath);
// GL.CreateShader will create an empty shader (obviously). The ShaderType enum denotes which type of shader will be created.
var vertexShader = GL.CreateShader(ShaderType.VertexShader);
// Now, bind the GLSL source code
GL.ShaderSource(vertexShader, shaderSource);
// And then compile
CompileShader(vertexShader);
// We do the same for the fragment shader.
if (inAssembly)
shaderSource = GetEmbeddedResource(fragPath);
else
shaderSource = File.ReadAllText(fragPath);
var fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
GL.ShaderSource(fragmentShader, shaderSource);
CompileShader(fragmentShader);
// These two shaders must then be merged into a shader program, which can then be used by OpenGL.
// To do this, create a program...
Handle = GL.CreateProgram();
// Attach both shaders...
GL.AttachShader(Handle, vertexShader);
GL.AttachShader(Handle, fragmentShader);
// And then link them together.
LinkProgram(Handle);
// When the shader program is linked, it no longer needs the individual shaders attached to it; the compiled code is copied into the shader program.
// Detach them, and then delete them.
GL.DetachShader(Handle, vertexShader);
GL.DetachShader(Handle, fragmentShader);
GL.DeleteShader(fragmentShader);
GL.DeleteShader(vertexShader);
// The shader is now ready to go, but first, we're going to cache all the shader uniform locations.
// Querying this from the shader is very slow, so we do it once on initialization and reuse those values
// later.
// First, we have to get the number of active uniforms in the shader.
GL.GetProgram(Handle, GetProgramParameterName.ActiveUniforms, out var numberOfUniforms);
// Next, allocate the dictionary to hold the locations.
_uniformLocations = new Dictionary<string, int>();
// Loop over all the uniforms,
for (var i = 0; i < numberOfUniforms; i++)
{
// get the name of this uniform,
var key = GL.GetActiveUniform(Handle, i, out _, out _);
// get the location,
var location = GL.GetUniformLocation(Handle, key);
// and then add it to the dictionary.
_uniformLocations.Add(key, location);
}
}
private static void CompileShader(int shader)
{
// Try to compile the shader
GL.CompileShader(shader);
// Check for compilation errors
GL.GetShader(shader, ShaderParameter.CompileStatus, out var code);
if (code != (int)All.True)
{
// We can use `GL.GetShaderInfoLog(shader)` to get information about the error.
var infoLog = GL.GetShaderInfoLog(shader);
throw new Exception($"Error occurred whilst compiling Shader({shader}).\n\n{infoLog}");
}
}
private static void LinkProgram(int program)
{
// We link the program
GL.LinkProgram(program);
// Check for linking errors
GL.GetProgram(program, GetProgramParameterName.LinkStatus, out var code);
if (code != (int)All.True)
{
// We can use `GL.GetProgramInfoLog(program)` to get information about the error.
throw new Exception($"Error occurred whilst linking Program({program})");
}
}
// A wrapper function that enables the shader program.
public void Use()
{
GL.UseProgram(Handle);
}
// The shader sources provided with this project use hardcoded layout(location)-s. If you want to do it dynamically,
// you can omit the layout(location=X) lines in the vertex shader, and use this in VertexAttribPointer instead of the hardcoded values.
public int GetAttribLocation(string attribName)
{
return GL.GetAttribLocation(Handle, attribName);
}
// Uniform setters
// Uniforms are variables that can be set by user code, instead of reading them from the VBO.
// You use VBOs for vertex-related data, and uniforms for almost everything else.
// Setting a uniform is almost always the exact same, so I'll explain it here once, instead of in every method:
// 1. Bind the program you want to set the uniform on
// 2. Get a handle to the location of the uniform with GL.GetUniformLocation.
// 3. Use the appropriate GL.Uniform* function to set the uniform.
/// <summary>
/// Set a uniform int on this shader.
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <param name="data">The data to set</param>
public void SetInt(string name, int data)
{
GL.UseProgram(Handle);
GL.Uniform1(_uniformLocations[name], data);
}
/// <summary>
/// Set a uniform float on this shader.
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <param name="data">The data to set</param>
public void SetFloat(string name, float data)
{
GL.UseProgram(Handle);
GL.Uniform1(_uniformLocations[name], data);
}
/// <summary>
/// Set a uniform Matrix4 on this shader
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <param name="data">The data to set</param>
/// <remarks>
/// <para>
/// The matrix is transposed before being sent to the shader.
/// </para>
/// </remarks>
public void SetMatrix4(string name, Matrix4 data)
{
GL.UseProgram(Handle);
GL.UniformMatrix4(_uniformLocations[name], true, ref data);
}
/// <summary>
/// Set a uniform Vector3 on this shader.
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <param name="data">The data to set</param>
public void SetVector3(string name, Vector3 data)
{
GL.UseProgram(Handle);
GL.Uniform3(_uniformLocations[name], data);
}
/// <summary>
/// Set a uniform Vector4 on this shader.
/// </summary>
/// <param name="name">The name of the uniform</param>
/// <param name="data">The data to set</param>
public void SetVector4(string name, Vector4 data)
{
GL.UseProgram(Handle);
GL.Uniform4(_uniformLocations[name], data);
}
}
型
我的顶点和碎片着色器是:
defaultVertex.shader
#version 330 core
// Positions/Coordinates
layout (location = 0) in vec3 aPos;
// Texture Coordinates
layout (location = 1) in vec2 aTex;
// Outputs the texture coordinates to the fragment shader
out vec2 texCoord;
uniform mat4 model; // disabled for debug purposes
uniform mat4 view;
uniform mat4 ortho;
void main()
{
// Outputs the positions/coordinates of all vertices
gl_Position = vec4(aPos, 1.0) /* model */* view * ortho;
// Assigns the texture coordinates from the Vertex Data to "texCoord"
texCoord = aTex;
}
型
defaultFrag.shader
#version 330 core
// Outputs colors in RGBA
out vec4 FragColor;
// Inputs the texture coordinates from the Vertex Shader
in vec2 texCoord;
// Gets the color
uniform vec4 color;
// Gets the Texture Unit from the main function
uniform sampler2D tex0;
void main()
{
FragColor = texture(tex0, texCoord) * color;
}
型
我已经学习了基础知识,适度地理解了open gl的工作原理,以及我应该如何在过去的3天内使用它,所以我真的没有资格解决这个问题,如果这是一个愚蠢的简单错误,我也道歉,我的代码数量和它的缺乏整洁。
我还想补充一点,我在GLFW.NET中强制使用OpenGL 4
Glfw.WindowHint(Hint.ContextVersionMajor, 4);
Glfw.WindowHint(Hint.ContextVersionMinor, 4);
型
我使用的窗口是800 x800,当前输出的背景颜色是清晰的:x1c 0d1x的数据
这些是我用c++教程渲染的三角形:
的
我尝试过的:
在片段着色器中禁用颜色倍增
禁用在位置顶部相乘的所有矩阵
使用不同的顶点和/或索引
将out FragColor设置为平坦值
2条答案
按热度按时间66bbxpm51#
顶点缓冲区的布局不是
字符串
但事实如此
型
因此,您需要更改顶点属性规范:
第一个月
vao.LinkAttrib(vbo, shader.GetAttribLocation("aTex"), 2, VertexAttribPointerType.Float, 5 * sizeof(float), 3 * sizeof(float));
个型
几何体的大小约为1像素,且位于窗口的左下角,因为顶点坐标在[-0.5,0.5]范围内,但您使用正交投影将体积从(0,0,0)投影到视口上的(
widthHeight.x
,widthHeight.y
,1000 f)。更改投影矩阵:orthoMatrix = Matrix4.CreateOrthographicOffCenter(0f, widthHeight.x, 0f, widthHeight.y, 0f, 1000f);
个型
dddzy1tm2#
已经有几天了,头疼得很厉害,但我让它起作用了。有两个主要问题导致无法渲染:使用
GL.Enable(EnableCap.DepthTest)
似乎导致所有内容都无法呈现。使用VertexAttribPointer
s的子缓冲区将不起作用,因为子缓冲区将tex坐标放置在顶点之后,而VertexAttribPointer
似乎将顶点与每个顶点的tex坐标沿着使用,而不是所有顶点,然后是所有tex坐标。(我不确定是不是这样,但是把顶点和tex坐标放在同一条“线”(顶点)上对我来说是有效的)。GL函数的执行顺序可能还有其他问题,但老实说,我不记得了...我将用这个答案将其标记为已解决,因此它不再被打开,并且我将把这段代码留在这里,以防有人(极不可能)需要它。
字符串
Texture类I不幸被删除,现在正在使用的Texture2D类要复杂得多,超出了这个答案的目的,它本质上与我在问题中添加的纹理类非常相似。Texture类包含一个
GetVertices
方法,它返回一个多维的顶点浮点数组(我这样做是为了更好地可视化它们,更容易将纹理坐标添加到顶点,最终变成一个一维的浮点数组)。这是我现在正在使用的代码的核心,但是它分布在多个类和方法等中。也可以被修改以适应其它目的。
如果这不是将线程标记为已解决的正确方法,或者这样做或代码有任何错误,请告诉我。我事先道歉,因为我不知道“如何堆栈溢出”。(我很少在这里提问或回答问题...)