
Issues converting OpenGL tutorials to OpenTK
Posted Sunday, 13 November, 2011 - 03:33 by Dr_Asik inI'm trying to convert these popular tutorials to OpenTK. The tutorial shows how to mix two textures and draw the result. For some reason when I do it in OpenTK, though, the texture seems translated slightly to the right and it repeats at the left. Like this:

As far as I can tell, I've translated the code in the tutorial quite literally.
Here's the fragment shader:
#version 330 uniform float fade_factor; uniform sampler2D textures[2]; in vec2 texcoord; out vec4 outputColor; void main() { outputColor = mix( texture2D(textures[0], texcoord), texture2D(textures[1], texcoord), fade_factor ); }
The vertex shader:
#version 330 in vec2 position; out vec2 texcoord; void main() { gl_Position = vec4(position, 0.0, 1.0); texcoord = position * vec2(0.5) + vec2(0.5); }
And the C# code:
using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Runtime.InteropServices; using OpenTK; using OpenTK.Graphics.OpenGL; using GL = OpenTK.Graphics.OpenGL.GL; namespace GLSL { class Game : GameWindow { struct Uniforms { public int[] TextureLocations; public int PositionLocation; public int FadeFactorLocation; public int[] Textures; public int Positions; public float FadeFactor; } float mFadeVariation = 0.01f; int mProgram; Uniforms mUniforms; int[] mVertexIndices = { 0, 1, 2, 3 }; int mVertexIndicesBuffer; /// <summary> /// Sets the window's size. /// </summary> public Game() : base(500, 500) { } /// <summary> /// Is called once when the window is first loaded. /// Any initialization is performed here. /// </summary> protected override void OnLoad(EventArgs e) { mUniforms.FadeFactor = 0.5f; mUniforms.Textures = new int[]{ CreateTexture("gl2-hello-1.png"), CreateTexture("gl2-hello-2.png") }; var vertexPositions = new float[] { // These are the vertex of the rectangle -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f }; mVertexIndicesBuffer = CreateBufferObject(mVertexIndices, BufferTarget.ElementArrayBuffer, BufferUsageHint.StreamDraw); mUniforms.Positions = CreateBufferObject(vertexPositions, BufferTarget.ArrayBuffer, BufferUsageHint.StreamDraw); mProgram = CreateProgram(new[] { CreateShader(ShaderType.VertexShader, "hello-gl.v.glsl"), CreateShader(ShaderType.FragmentShader, "hello-gl.f.glsl") }); mUniforms.TextureLocations = new int[] { GetUniformLocation("textures[0]"), GetUniformLocation("textures[1]") }; mUniforms.PositionLocation = GetAttribLocation("position"); mUniforms.FadeFactorLocation = GetUniformLocation("fade_factor"); } /// <summary> /// Draws the image onscreen. Is called periodically by GameWindow.Run(). /// </summary> protected override void OnRenderFrame(FrameEventArgs e) { mUniforms.FadeFactor += mFadeVariation; if (mUniforms.FadeFactor < 0.0f || mUniforms.FadeFactor > 1.0f) { mFadeVariation = -mFadeVariation; mUniforms.FadeFactor += 2 * mFadeVariation; } GL.ClearColor(Color.Black); GL.Clear(ClearBufferMask.ColorBufferBit); GL.UseProgram(mProgram); GL.Uniform1(mUniforms.FadeFactorLocation, mUniforms.FadeFactor); GL.ActiveTexture(TextureUnit.Texture0); GL.BindTexture(TextureTarget.Texture2D, mUniforms.Textures[0]); GL.Uniform1(mUniforms.TextureLocations[0], 0); GL.ActiveTexture(TextureUnit.Texture1); GL.BindTexture(TextureTarget.Texture2D, mUniforms.Textures[1]); GL.Uniform1(mUniforms.TextureLocations[1], 1); GL.BindBuffer(BufferTarget.ArrayBuffer, mUniforms.Positions); GL.EnableVertexAttribArray(mUniforms.PositionLocation); GL.BindBuffer(BufferTarget.ElementArrayBuffer, mVertexIndicesBuffer); GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 0, 0); GL.DrawElements(BeginMode.TriangleStrip, mVertexIndices.Length, DrawElementsType.UnsignedInt, 0); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0); GL.DisableVertexAttribArray(0); GL.UseProgram(0); SwapBuffers(); } /// <summary> /// Is called when the window is resized. /// </summary> protected override void OnResize(EventArgs e) { // Easiest way to respect aspect ratio if (Width > Height) { GL.Viewport(0, 0, Height, Height); } else { GL.Viewport(0, 0, Width, Width); } } /// <summary> /// Compile a shader from source /// </summary> /// <returns>OpenGL handle to the compiled shader</returns> int CreateShader(ShaderType shaderType, string fileName) { int shader = GL.CreateShader(shaderType); var source = File.ReadAllText(fileName); GL.ShaderSource(shader, source); GL.CompileShader(shader); int status; GL.GetShader(shader, ShaderParameter.CompileStatus, out status); if (status == 0) { var errorLog = GL.GetShaderInfoLog(shader); throw new Exception(String.Format("Compile failure in {0}: {1}\n", fileName, errorLog)); } return shader; } /// <summary> /// Links compiled shaders to create a program /// </summary> /// <param name="shaders">Compiled shaders</param> /// <returns>OpenGL handle to the program</returns> int CreateProgram(IEnumerable<int> shaders) { int program = GL.CreateProgram(); foreach (var shader in shaders) { GL.AttachShader(program, shader); } GL.LinkProgram(program); int status; GL.GetProgram(program, ProgramParameter.LinkStatus, out status); if (status == 0) { throw new Exception(String.Format("Linker failure : {0}", GL.GetProgramInfoLog(program))); } return program; } /// <summary> /// Allocates and assigns data to a new VBO /// </summary> /// <typeparam name="T">Basic data type of the vertex data</typeparam> /// <param name="buffer">An array containing vertex data</param> /// <returns>OpenGL handle to the new VBO</returns> int CreateBufferObject<T>(T[] buffer, BufferTarget target, BufferUsageHint usageHint) where T : struct { int handle; GL.GenBuffers(1, out handle); GL.BindBuffer(target, handle); GL.BufferData(target, (IntPtr)(Marshal.SizeOf(typeof(T)) * buffer.Length), buffer, usageHint); GL.BindBuffer(target, 0); return handle; } int CreateTexture(string fileName) { int handle; GL.GenTextures(1, out handle); GL.BindTexture(TextureTarget.Texture2D, handle); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); var bmp = new Bitmap(fileName); var ms = new MemoryStream(); bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb8, bmp.Width, bmp.Height, 0, PixelFormat.Rgb, PixelType.UnsignedByte, ms.ToArray()); return handle; } int GetUniformLocation(string uniform) { return GetLocation(uniform, GL.GetUniformLocation); } int GetAttribLocation(string attrib) { return GetLocation(attrib, GL.GetAttribLocation); } int GetLocation(string name, Func<int, string, int> glFunc) { int rv = glFunc(mProgram, name); if (rv == -1) { throw new Exception("Could not find location of " + name); } return rv; } } }
Any thoughts?


Comments
Re: Issues converting OpenGL tutorials to OpenTK
I've looked at TexLib.cs and that gave me some ideas. When I generate the texture, I currently use a memory stream as temporary buffer:
I've tried simply locking the bits and passing that to GL.TexImage2D instead:
The result is that there is no more translation, the texture is properly aligned. However, it is upside-down. I have no idea why this method works and not the other, and I don't see why the image is upside down in this case and not the other. ?
Re: Issues converting OpenGL tutorials to OpenTK
Good idea to port this example to OpenTK. :)
I was browsing some archives and found this, it's from the Bitiopia site of MiguelTK, check him out, he rocks!
// The Bitmap class loads images and orients them top-down.
// OpenGL expects bitmap images to be oriented bottom-up.
bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
Re: Issues converting OpenGL tutorials to OpenTK
Your vertex shader flips the texcoords. E.g. (-1.0, -1.0) is the left-bottom vertex but it gets (0, 0) as the texcoord.
To get the correct result change the calculation to:
texcoord = vec2(position.x * 0.5 + 0.5, 1 - (position.y * 0.5 + 0.5));