OpenGL" Hello world"

时间:2017-01-25 09:55:44

标签: opengl f# opentk

像往常一样,当我尝试用现代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是可以的。

但对于那些白天做的人来说,可能很容易发现问题出在哪里。

1 个答案:

答案 0 :(得分:4)

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

您可以使用经典方式启用/禁用顶点属性数组,如下所示:

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

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

这会在我的NVidia GTX 760桌面上生成正确颜色的三角形,因为它会对当前活动的顶点阵列信息起作用。

我建议您再次查看使用状态机的方式。启用顶点数组并通过VertexAttribPointer定义其结构是特定于程序的并且属于一起。通常,您将使用VAO对此信息进行分组,并在绘制完成后将其解除绑定。如果顶点属性数组应该是全局状态,那么禁用它们是没有意义的,这个事实应该有详细记录。因此,代码存在开发看似无关的函数之间的虚假交互的危险,因为它共享OpenGL状态机的复杂状态。

可能的方法是:

  • 设置

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

    • 创建缓冲区,初始化数据,然后保存其句柄并使其不受约束

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

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

    • BIND VAO
    • 绑定更改/附加缓冲区(如有必要)
    • 绑定程序
    • 打电话
    • 再次绑定所有内容(如果错误地依赖于OpenGL状态,则快速失败)

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

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