OpenGL 中未使用的颜色数据 "Hello world"

Color data not used in OpenGL "Hello world"

像往常一样,当我尝试使用现代 OpenGL,使用我可以在某些博客上找到的一个聪明的演示时,出现了问题。

预期行为:绘制一个三角形,颜色应在 3 个顶点之间进行插值。

发现行为:三角形是红色的。我将哪种颜色写入颜色数组并不重要。

代码优先(抱歉 160 行 - 现代 OpenGL 是垃圾...)。

open System
open System.Drawing
open System.Collections.Generic

open OpenTK
open OpenTK.Graphics
open OpenTK.Graphics.OpenGL
open OpenTK.Input

module Shaders =
    let vertexShader = 
        """#version 330

in vec3 vPosition;
in  vec3 vColor;
out vec4 color;
uniform mat4 modelview;

void
main()
{
    gl_Position = modelview * vec4(vPosition, 1.0);

    color = vec4( vColor, 1.0);
}
"""
    let fragmentShader = 
        """#version 330

in vec4 color;
out vec4 outputColor;

void
main()
{
  outputColor = color;
}
"""
    let initShaders () : int =
        let makeShader shaderType src =
            let sh = GL.CreateShader(shaderType)
            GL.ShaderSource(sh,src)
            GL.CompileShader(sh)
            sh
        let pgmId = GL.CreateProgram()
        let vsh = makeShader ShaderType.VertexShader vertexShader
        let fsh = makeShader ShaderType.FragmentShader fragmentShader
        GL.AttachShader(pgmId,vsh)
        GL.AttachShader(pgmId,fsh)
        GL.LinkProgram(pgmId)
        pgmId

let failMinusOne = function
    | -1 -> failwith "Something is -1 which should not be -1!"
    | x -> x

type Game(width,height) =
    inherit GameWindow(width, height, GraphicsMode.Default, "F# OpenTK Sample")
    do base.VSync <- VSyncMode.On

    let mutable shaderProgramId = -1
    let mutable attribute_vcol = -1
    let mutable attribute_vpos = -1
    let mutable uniform_mview = -1

    let mutable vbo_col = 0
    let mutable vbo_pos = 0

    let vertex_data = 
        [|
            Vector3(-0.8f, -0.8f, 0.f)
            Vector3( 0.8f, -0.8f, 0.f)
            Vector3( 0.f,  0.8f, 0.f)
        |]

    let col_data =
        [|
            Vector3( 1.f, 1.f, 1.f)
            Vector3( 0.f, 0.f, 1.f)
            Vector3( 0.f, 1.f, 0.f)
        |]

    let mview_data = [| Matrix4.Identity |]

    /// <summary>Load resources here.</summary>
    /// <param name="e">Not used.</param>
    override o.OnLoad e =
        base.OnLoad(e)
        o.Title <- "Hello OpenTK!"
        shaderProgramId <- Shaders.initShaders ()

        GL.ClearColor(Color.CornflowerBlue)
        GL.Enable(EnableCap.DepthTest)

        attribute_vpos <- GL.GetAttribLocation(shaderProgramId, "vPosition") |> failMinusOne 
        attribute_vcol <- GL.GetAttribLocation(shaderProgramId, "vColor") |> failMinusOne
        uniform_mview <- GL.GetUniformLocation(shaderProgramId, "modelview") |> failMinusOne

        vbo_col <- GL.GenBuffer()
        vbo_pos <- GL.GenBuffer()

    /// <summary>
    /// Called when your window is resized. Set your viewport here. It is also
    /// a good place to set up your projection matrix (which probably changes
    /// along when the aspect ratio of your window).
    /// </summary>
    /// <param name="e">Not used.</param>
    override o.OnResize e =
        base.OnResize e
        GL.Viewport(base.ClientRectangle.X, base.ClientRectangle.Y, base.ClientRectangle.Width, base.ClientRectangle.Height)
        let projection = Matrix4.CreatePerspectiveFieldOfView(float32 (Math.PI / 4.), float32 base.Width / float32 base.Height, 1.f, 64.f)
        GL.MatrixMode(MatrixMode.Projection)
        GL.LoadMatrix(ref projection)


    /// <summary>
    /// Called when it is time to setup the next frame. Add you game logic here.
    /// </summary>
    /// <param name="e">Contains timing information for framerate independent logic.</param>
    override o.OnUpdateFrame e =
        base.OnUpdateFrame e
        if base.Keyboard.[Key.Escape] then base.Close()
        else
            GL.BindBuffer(BufferTarget.ArrayBuffer,vbo_col)
            GL.BufferData<Vector3>(BufferTarget.ArrayBuffer,Array.length col_data * Vector3.SizeInBytes,col_data,BufferUsageHint.StaticDraw)
            GL.VertexAttribPointer(attribute_vcol, 3, VertexAttribPointerType.Float, false, 0, 0)

            GL.BindBuffer(BufferTarget.ArrayBuffer,vbo_pos)
            GL.BufferData<Vector3>(BufferTarget.ArrayBuffer,Array.length vertex_data * Vector3.SizeInBytes,vertex_data,BufferUsageHint.StaticDraw )
            GL.VertexAttribPointer(attribute_vpos, 3, VertexAttribPointerType.Float, false, 0, 0)

            GL.UniformMatrix4(uniform_mview,false,ref mview_data.[0])
            GL.BindBuffer(BufferTarget.ArrayBuffer,0)

            GL.UseProgram(shaderProgramId)

    /// <summary>
    /// Called when it is time to render the next frame. Add your rendering code here.
    /// </summary>
    /// <param name="e">Contains timing information.</param>
    override o.OnRenderFrame(e) =
        base.OnRenderFrame e
        GL.Clear(ClearBufferMask.ColorBufferBit ||| ClearBufferMask.DepthBufferBit);
        GL.EnableVertexArrayAttrib(attribute_vpos,0)
        GL.EnableVertexArrayAttrib(attribute_vcol,0)

        GL.DrawArrays(PrimitiveType.Triangles,0,Array.length vertex_data)

        GL.DisableVertexArrayAttrib(attribute_vpos,0)
        GL.DisableVertexArrayAttrib(attribute_vcol,0)

        GL.Flush()
        base.SwapBuffers()

[<EntryPoint>]
let main argv = 
    use game = new Game(800,600)
    do game.Run(30.,30.)
    0 // return an integer exit code

在尝试找出问题所在几个小时后,我 运行 失去了想法。 添加更多三角形似乎也失败了。但是,三角形显示为顶点的事实让我觉得,将数据下载到 gpu 是可以的。

但是对于那些日复一日地这样做的人来说,可能很容易发现哪里出了问题。

您正在使用 OpenGL 4.5 函数 EnableVertexArrayAttrib 在 ID 为 0 的顶点数组对象 (VAO) 上启用顶点属性数组 。这是很奇怪的,因为你的着色器被版本化为 330,这是非常非常旧的,但更重要的是无效,因为你没有使用任何 VAO。

您可以 enable/disable 顶点属性数组的经典方式,如下所示:

GL.EnableVertexAttribArray(attribute_vpos)
GL.EnableVertexAttribArray(attribute_vcol)

GL.DisableVertexAttribArray(attribute_vpos)
GL.DisableVertexAttribArray(attribute_vcol)

这会在我的 NVidia GTX 760 桌面上生成正确着色的三角形,因为它作用于当前活动的顶点数组信息。

我建议再看看您使用状态机的方式。通过 VertexAttribPointer 启用顶点数组并定义它们的结构是 program-specific 并且属于一起。通常,您会使用 VAO 对这些信息进行分组,并在绘图完成后解除绑定。如果顶点属性数组应该是全局状态,那么禁用它们是没有意义的,这个事实应该是 well-documented。照原样,代码有在看似无关的函数之间形成虚假交互的危险,因为它共享 OpenGL 状态机的复杂状态。

一种可能的方法是:

  • 设置

    • 创建着色器,保留着色器程序句柄和属性位置

    • 创建缓冲区,初始化数据,然后保存它们的句柄并保持它们未绑定

    • 将顶点数组结构创建为 VAO,然后保存其句柄并使其保持未绑定状态

      • 对于每个顶点数组,启用并指定(例如 VertexAttribPointer)
  • 绘图

    • 绑定 VAO
    • 绑定 changing/additional 缓冲区(如果需要)
    • 绑定程序
    • 绘图调用
    • 再次解除绑定(如果错误地依赖 OpenGL 状态,则快速失败)

像这样的方法以更结构化的方式将与状态机的交互分组,并减少程序对保持不变的 OpenGL 状态机值的依赖。

不需要各种可变值并将它们初始化为 -1。它们可以按顺序绑定,直接分配正确的句柄(假设 OpenGL 上下文已经存在;请参阅评论。)