如何使用OpenGL创建自定义顶点格式

Azz*_*777 6 c# opengl glsl vertices opentk

我正在使用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
Byte 5: Color G 
Byte 6: Color B
Byte 7: Texture Coordinate Y
Run Code Online (Sandbox Code Playgroud)

以下是顶点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;
}
Run Code Online (Sandbox Code Playgroud)

作为每个轴的位置的字节是充足的,因为我只需要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;
}
Run Code Online (Sandbox Code Playgroud)

为了尝试找出问题,我使顶点着色器尽可能简单.用于编译着色器的代码使用立即模式绘制进行测试,并且它可以正常工作,因此不可能.

这是我的函数,它使用数据生成,设置和填充顶点缓冲区,并建立指向属性的指针.

    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);
    }
Run Code Online (Sandbox Code Playgroud)

根据我的理解,这是正确的过程:生成顶点数组对象并绑定它生成顶点缓冲区并绑定它用数据填充顶点缓冲区设置属性指针

着色器.*在编译和使用着色器后,使用此代码设置DataID.

PositionDataID = GL.GetAttribLocation(shaderProgram, "pos_data");
ColorDataID = GL.GetAttribLocation(shaderProgram, "col_data");
Run Code Online (Sandbox Code Playgroud)

这是我的渲染功能:

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();
}
Run Code Online (Sandbox Code Playgroud)

任何人都可以如此善良地给我一些指示(没有双关语)?我是以错误的顺序做这个还是有一些我需要打电话的功能?

我在网上搜索过,但找不到一个很好的教程或指南,解释如何实现自定义顶点.如果您需要更多信息,请说明.

Chr*_*ica 10

制作自己的顶点格式并不多.这一切都是在glVertexAttribPointer电话中完成的.首先,你使用4作为步幅参数,但你的顶点结构是8字节宽,所以从一个顶点的开始到下一个顶点有8个字节,所以步幅必须是8(当然在两个调用中).偏移是正确的,但你应该将颜色的标准化标志设置为true,因为你肯定希望它们在[0,1]范围内(我不知道这是否也应该是顶点位置的情况) ).

接下来,在着色器中使用自定义顶点属性时,不要启用已弃用的固定函数数组(gl...ClienState事物).相反,你必须使用

GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);
Run Code Online (Sandbox Code Playgroud)

和相应的glDisableVertexAttribArray电话.

呼叫中的count/4意思是什么glDrawArrays?请记住,最后一个参数指定顶点数而不是基元(在您的情况下为四边形).但也许它是这样的.

除了这些真正的错误,你不应该使用你必须在着色器中解码它的复制顶点格式.这就是步幅和偏移参数的glVertexAttribPointer用途.例如,重新定义顶点数据:

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;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用

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);
Run Code Online (Sandbox Code Playgroud)

在你的着色器中

attribute vec3 pos_data;
attribute vec3 col_data;
attribute vec2 tex_data;
Run Code Online (Sandbox Code Playgroud)

而且您不必自己从位置和颜色中提取纹理坐标.

你应该考虑你的空间需求是否确实需要使用字节来表示顶点位置,因为这极大地限制了位置数据的精度.也许短裤或半精度花车将是一个很好的妥协.

并且也不需要调用glBindBufferrender方法,因为这只需要glVertexAttribPointer并且保存在被激活的VAO中glBindVertexArray.您通常也不应该调用,glFlush因为当交换缓冲区时,操作系统无论如何都会这样做(假设您使用双缓冲).

最后但同样重要的是,请确保您的硬件还支持您正在使用的所有功能(如VBO和VAO).

编辑:实际上,数组的启用标志也存储在VAO中,以便您可以调用

GL.EnableVertexAttribArray(Shaders.PositionDataID);
GL.EnableVertexAttribArray(Shaders.ColorDataID);
Run Code Online (Sandbox Code Playgroud)

SetData方法中(当然,在创建和绑定VAO之后),然后glBindVertexArray在渲染函数中绑定VAO时启用它们.哦,我刚看到另一个错误.在渲染函数中绑定VAO时,属性数组的已启用标志将被VAO中的状态覆盖,并且在VAO创建后未启用它们,它们仍处于禁用状态.所以你必须这样做,在SetData方法中启用数组.实际上在你的情况下,你可能很幸运,当你在渲染功能中启用数组时,VAO仍然是绑定的(因为你没有调用glBindVertexArray(0)),但你不应该指望它.