C# 如何使用OpenGL创建自定义顶点格式
我正在使用OpenTK编写自己的引擎(基本上只是C#的OpenGL绑定,gl*变成gl.*),我将存储大量顶点缓冲区,每个缓冲区中有数千个顶点。因此,我需要自己的自定义顶点格式,因为带有浮动的Vec3只会占用太多空间。(我这里说的是数百万个顶点) 我要做的是使用此布局创建自己的顶点格式:C# 如何使用OpenGL创建自定义顶点格式,c#,opengl,glsl,opentk,vertices,C#,Opengl,Glsl,Opentk,Vertices,我正在使用OpenTK编写自己的引擎(基本上只是C#的OpenGL绑定,gl*变成gl.*),我将存储大量顶点缓冲区,每个缓冲区中有数千个顶点。因此,我需要自己的自定义顶点格式,因为带有浮动的Vec3只会占用太多空间。(我这里说的是数百万个顶点) 我要做的是使用此布局创建自己的顶点格式: Byte 0: Position X Byte 1: Position Y Byte 2: Position Z Byte 3: Texture Coordinate X Byte 4: Color R By
Byte 0: Position X
Byte 1: Position Y
Byte 2: Position Z
Byte 3: Texture Coordinate X
Byte 4: Color R
Byte 5: Color G
Byte 6: Color B
Byte 7: Texture Coordinate Y
以下是顶点的C#代码:
public struct SmallBlockVertex
{
public byte PositionX;
public byte PositionY;
public byte PositionZ;
public byte TextureX;
public byte ColorR;
public byte ColorG;
public byte ColorB;
public byte TextureY;
}
每个轴的位置都有一个字节,因为我只需要32^3个唯一的位置
我已经编写了我自己的顶点着色器,它将两个vec4作为输入,为每一组字节启用。
我的顶点着色器如下所示:
attribute vec4 pos_data;
attribute vec4 col_data;
uniform mat4 projection_mat;
uniform mat4 view_mat;
uniform mat4 world_mat;
void main()
{
vec4 position = pos_data * vec4(1.0, 1.0, 1.0, 0.0);
gl_Position = projection_mat * view_mat * world_mat * position;
}
为了尝试隔离问题,我将顶点着色器设置得尽可能简单。
编译着色器的代码使用即时模式绘图进行了测试,它可以正常工作,所以不可能是这样
下面是我的函数,它生成、设置并用数据填充顶点缓冲区,并建立指向属性的指针
public void SetData<VertexType>(VertexType[] vertices, int vertexSize) where VertexType : struct
{
GL.GenVertexArrays(1, out ArrayID);
GL.BindVertexArray(ArrayID);
GL.GenBuffers(1, out ID);
GL.BindBuffer(BufferTarget.ArrayBuffer, ID);
GL.BufferData<VertexType>(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * vertexSize), vertices, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(Shaders.PositionDataID, 4, VertexAttribPointerType.UnsignedByte, false, 4, 0);
GL.VertexAttribPointer(Shaders.ColorDataID, 4, VertexAttribPointerType.UnsignedByte, false, 4, 4);
}
这是我的渲染函数:
void Render()
{
GL.UseProgram(Shaders.ChunkShaderProgram);
Matrix4 view = Constants.Engine_Physics.Player.ViewMatrix;
GL.UniformMatrix4(Shaders.ViewMatrixID, false, ref view);
//GL.Enable(EnableCap.DepthTest);
//GL.Enable(EnableCap.CullFace);
GL.EnableClientState(ArrayCap.VertexArray);
{
Matrix4 world = Matrix4.CreateTranslation(offset.Position);
GL.UniformMatrix4(Shaders.WorldMatrixID, false, ref world);
GL.BindVertexArray(ArrayID);
GL.BindBuffer(OpenTK.Graphics.OpenGL.BufferTarget.ArrayBuffer, ID);
GL.DrawArrays(OpenTK.Graphics.OpenGL.BeginMode.Quads, 0, Count / 4);
}
//GL.Disable(EnableCap.DepthTest);
//GL.Disable(EnableCap.CullFace);
GL.DisableClientState(ArrayCap.VertexArray);
GL.Flush();
}
有谁能给我指点一下吗(没有双关语的意思)?我是否按错误的顺序执行此操作,或者是否需要调用某些函数
我在网上搜索过,但找不到一个好的教程或指南来解释如何实现自定义顶点。
如果您需要更多信息,请说出来。制作自己的顶点格式没有多少困难。这一切都是在
glvertexattributepointer
调用中完成的。首先,您使用4作为步幅参数,但是您的顶点结构是8字节宽的,因此从一个顶点到下一个顶点有8字节,因此步幅必须是8(当然,在两个调用中)。偏移是正确的,但是您应该为颜色设置规格化标志为true,因为您肯定希望它们在[0,1]范围内(我不知道顶点位置是否也应该如此)
接下来,在着色器中使用自定义顶点属性时,不会启用不推荐使用的固定函数数组(gl…ClienState)。相反,你必须使用
GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);
以及相应的glDisableVertexAttributeArray
调用
在glDrawArrays
调用中,count/4
意味着什么。请记住,最后一个参数指定的是顶点的数量,而不是基本体(本例中为四边形)。但也许这是有意的
除了这些真正的错误之外,您不应该使用复杂的顶点格式,因为您必须自己在着色器中对其进行解码。这就是glvertexattributepointer
的步幅和偏移参数的作用。例如,稍微重新定义顶点数据:
public struct SmallBlockVertex
{
public byte PositionX;
public byte PositionY;
public byte PositionZ;
public byte ColorR;
public byte ColorG;
public byte ColorB;
public byte TextureX;
public byte TextureY;
}
然后你就可以使用
GL.VertexAttribPointer(Shaders.PositionDataID, 3, VertexAttribPointerType.UnsignedByte, false, 8, 0);
GL.VertexAttribPointer(Shaders.ColorDataID, 3, VertexAttribPointerType.UnsignedByte, true, 8, 3);
GL.VertexAttribPointer(Shaders.TexCoordDataID, 2, VertexAttribPointerType.UnsignedByte, true, 8, 6);
在着色器中,你有
attribute vec3 pos_data;
attribute vec3 col_data;
attribute vec2 tex_data;
你不必自己从位置和颜色中提取纹理坐标
你应该认真考虑你的空间需求是否真的需要使用字节作为顶点位置,因为这极大地限制了你位置数据的精度。也许做空或半精度浮动将是一个很好的折衷方案
而且,在render方法中调用glBindBuffer
也不是必需的,因为这只对GLVertexAttributePointer
需要,并且保存在由glBindVertexArray
激活的VAO中。通常也不应调用glFlush
,因为在交换缓冲区时,操作系统会执行此操作(假设使用双缓冲)
最后但并非最不重要的一点是,确保您的硬件也支持您正在使用的所有功能(如VBOs和VAOs)
编辑:实际上,数组的已启用标志也存储在VAO中,以便您可以调用
GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);
在
SetData
方法中(当然,在创建和绑定VAO之后),然后通过渲染函数中的glBindVertexArray
绑定VAO时,它们将被启用。哦,我刚刚看到另一个错误。在渲染函数中绑定VAO时,属性数组的已启用标志将被VAO中的状态覆盖,并且由于在创建VAO后未启用它们,因此它们仍然处于禁用状态。因此,您必须像前面所说的那样,在SetData
方法中启用数组。实际上,在您的情况下,您可能很幸运,当您在渲染函数中启用数组时(因为您没有调用glBindVertexArray(0)
),VAO仍然是绑定的,但您不应该指望这一点。。。。当你运行它时会发生什么?在任何情况下,您都应该让系统处于工作状态。如果您知道如何使用浮点数据,请使用它。让它工作起来。一旦正确渲染了内容,就可以慢慢优化数据。在每一步中,验证它是否有效。然后,当你到达它断裂的点时,你知道哪里出了问题。如果我运行它,什么也不会发生。也就是说,它运行,但不显示任何内容。我还将尝试使用浮点数据并从中进行开发,但问题是,我不知道如何才能使自己的顶点格式正常工作(因此标题)。这是一个重大而关键的步骤(可能无法划分为更小的步骤),其中许多事情可能会出错。所以这基本上只是尝试和错误。如果你知道怎么做,你能在我的代码中发现任何错误吗?谢谢你的详细回复。这解释了很多!我明天会看一看,然后回来汇报。我在每个位置轴中只需要一个字节的原因是因为每个顶点都是大小为32*32*32的块的一部分,我只需要显示整数位置。然后我用平移矩阵偏移顶点。@Azzi777更新了我的答案,发现了另一个可能的问题。
GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);