As usual when I try to get my fingers wet with modern OpenGL, using one of the clever demos I can find on some blogs, something goes wrong.
Expected behavior: Draw a triangle and the colors should be interpolated between the 3 vertices.
Found behavior: Triangle is red. And it does not matter which colors I write into the color array.
Code first (sorry 160 lines - modern OpenGL is spammy...).
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
After a few hours trying to find what is going wrong I ran out of ideas. Adding more triangles also seems to flop. But then, the fact that the triangle shows as the vertices suggest, makes me think, the download of the data to the gpu is okay-ish.
But for you guys who do that day in day out, it will probably be easy to spot where things go wrong.