sanchiski's picture

My first try, render to 2d texture to quad and GLSL

Hello everyone, this is my first ever experiment with OpenTK. I am trying to render a 2d bitmap into a quad and apply some effect with GLSL. I think I am quite near, but so far I only get a black screen, can anyone spot what I am doing wrong? thanks in advance.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using OpenTK;
using OpenTK.Input;
using System.Drawing;
using System.Drawing.Imaging;
using GL = OpenTK.Graphics.OpenGL.GL;
using OpenGL = OpenTK.Graphics.OpenGL;
 
namespace ThumbMaker
{
 
    public class Demo : GameWindow
    {
        private int textureId;
        int vertex_shader_object, fragment_shader_object, shader_program;
        int vertex_buffer_object, color_buffer_object, element_buffer_object;
 
        public Demo()
            : base(800, 600, OpenTK.Graphics.GraphicsMode.Default, "Quad Test")
        {
        }
 
        protected override void OnLoad(EventArgs e)
        {
            OnResize(null);
 
            GL.ClearColor(Color.Black);
 
            String vs = "attribute vec4 a_position;" +
			            "attribute vec2 a_texcoord;" +
			            "varying vec2 v_texcoord;" +
			            "void main()" +
			            "{" +
			            "	v_texcoord = a_texcoord.st;" +
			            "	gl_Position = a_position;" +
			            "}";
 
            String fs = "varying vec2 v_texcoord; " +
                        "uniform sampler2D u_texture_y; " +
                        "void main()" +
                        "{" +
                        "   vec3 color = texture2D(u_texture_y, v_texcoord).rgb;" +
                        "   gl_FragColor.rgb = color;"+
                        "}";
 
            CreateShaders(vs, fs, out vertex_shader_object, out fragment_shader_object, out shader_program);
 
            InitTexturing();
            textureId = CreateTexture();
 
            GL.ActiveTexture(OpenTK.Graphics.OpenGL.TextureUnit.Texture0);
            GL.BindTexture(OpenTK.Graphics.OpenGL.TextureTarget.Texture2D, textureId);
 
            int textureHandle = GL.GetUniformLocation(shader_program, "u_texture_y");
            GL.Uniform1(textureHandle, 0);
        }
 
        private static void InitTexturing()
        {
            GL.Disable(OpenGL.EnableCap.CullFace);
            GL.Enable(OpenGL.EnableCap.Texture2D);
            GL.Enable(OpenGL.EnableCap.Blend);
            GL.BlendFunc(OpenGL.BlendingFactorSrc.SrcAlpha, OpenGL.BlendingFactorDest.OneMinusSrcAlpha);
            GL.PixelStore(OpenGL.PixelStoreParameter.UnpackAlignment, 1);
        }
 
        private static int CreateTexture()
        {
            // Load the bitmap from the resources
            Bitmap bitmap = global::TestApp.Properties.Resources.TestBitmap;
 
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
            int tex = GL.GenTexture();
            GL.BindTexture(OpenGL.TextureTarget.Texture2D, tex);
 
            GL.BindTexture(OpenGL.TextureTarget.Texture2D, tex);
            GL.TexImage2D(OpenGL.TextureTarget.Texture2D, 0, OpenGL.PixelInternalFormat.Rgba, data.Width, data.Height, 0, OpenGL.PixelFormat.Bgra, OpenGL.PixelType.UnsignedByte, data.Scan0);
 
            bitmap.UnlockBits(data);
 
            GL.TexParameter(OpenGL.TextureTarget.Texture2D, OpenGL.TextureParameterName.TextureMinFilter, (int)OpenGL.TextureMinFilter.Linear);
            GL.TexParameter(OpenGL.TextureTarget.Texture2D, OpenGL.TextureParameterName.TextureMagFilter, (int)OpenGL.TextureMagFilter.Linear);
 
            return tex;
        }
 
        protected override void OnUpdateFrame(FrameEventArgs e)
        {
            if (Keyboard[Key.Escape])
                Exit();
 
            if (Keyboard[Key.Space])
            {
                OpenGL.ErrorCode err = GL.GetError();
                Console.WriteLine(err + "  " + OpenTK.Graphics.Glu.ErrorString((OpenTK.Graphics.GluErrorCode)err));
                Console.WriteLine("GL error code: {0}", err);
            }
        }
 
        protected override void OnResize(EventArgs e)
        {
            GL.Viewport(new System.Drawing.Size(Width, Height));
            GL.MatrixMode(OpenGL.MatrixMode.Projection);
            GL.LoadIdentity();
            GL.Ortho(0, Width, 0, Height, -1, 1);
 
            GL.MatrixMode(OpenGL.MatrixMode.Modelview);
        }
 
        protected override void OnRenderFrame(FrameEventArgs e)
        {
            GL.Clear(OpenGL.ClearBufferMask.ColorBufferBit);
            GL.MatrixMode(OpenGL.MatrixMode.Modelview);
            GL.LoadIdentity();
 
            GL.DrawArrays(OpenGL.BeginMode.TriangleStrip, 0, 4);
 
            SwapBuffers();
        }
 
        void CreateShaders(string vs, string fs, out int vertexObject, out int fragmentObject, out int program)
        {
            int status_code;
            string info;
 
            vertexObject = GL.CreateShader(OpenGL.ShaderType.VertexShader);
            fragmentObject = GL.CreateShader(OpenGL.ShaderType.FragmentShader);
 
            // Compile vertex shader
            GL.ShaderSource(vertexObject, vs);
            GL.CompileShader(vertexObject);
            GL.GetShaderInfoLog(vertexObject, out info);
            GL.GetShader(vertexObject, OpenGL.ShaderParameter.CompileStatus, out status_code);
 
            if (status_code != 1)
                throw new ApplicationException(info);
 
            // Compile vertex shader
            GL.ShaderSource(fragmentObject, fs);
            GL.CompileShader(fragmentObject);
            GL.GetShaderInfoLog(fragmentObject, out info);
            GL.GetShader(fragmentObject, OpenGL.ShaderParameter.CompileStatus, out status_code);
 
            if (status_code != 1)
                throw new ApplicationException(info);
 
            program = GL.CreateProgram();
            GL.AttachShader(program, fragmentObject);
            GL.AttachShader(program, vertexObject);
 
            GL.LinkProgram(program);
 
            float[] vertexVertices  = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f };
            float[] textureVertices = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f };
 
            int handleAPosition = GL.GetAttribLocation(program, "a_position");
            int handleATexcoord = GL.GetAttribLocation(program, "a_texcoord");
 
            GL.EnableVertexAttribArray(handleAPosition);
            GL.EnableVertexAttribArray(handleATexcoord);
 
            GL.VertexAttribPointer(handleAPosition, 2, OpenGL.VertexAttribPointerType.Float, false, 0, vertexVertices);
            GL.VertexAttribPointer(handleATexcoord, 2, OpenGL.VertexAttribPointerType.Float, false, 0, textureVertices);
 
            GL.UseProgram(program);
        }
    }
 
    static class Program
    {
        static void Main(string[] args)
        {
            using (Demo demo = new Demo())
            {
                demo.Run(1.0, 0.0);
            }
        }
    }
}

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
Snorkel's picture

You aren't assigning any value in to the alpha channel of the shader output.
So GL.BlendFunc(OpenGL.BlendingFactorSrc.SrcAlpha, OpenGL.BlendingFactorDest.OneMinusSrcAlpha);
Can't work.

Fix:

String fs = "varying vec2 v_texcoord; " +
                        "uniform sampler2D u_texture_y; " +
                        "void main()" +
                        "{" +
                        "   vec4 color;" +
                        "   color.rgba = texture2D(u_texture_y, v_texcoord).rgba;" +                        
                        "   gl_FragColor = color;" +
                        "}";
Snorkel's picture

Some other comments I have on your code :p

- You don't need these:

using GL = OpenTK.Graphics.OpenGL.GL;
using OpenGL = OpenTK.Graphics.OpenGL;

- I rather write GL.Enable(EnableCap.Texture2D), shorter to type easier to read.

- don't use matrix mode GL.MatrixMode(OpenGL.MatrixMode.Modelview);
this only used for fixed pipeline ( so when you're not using shaders )

- also (could be wrong here) I don't think you need this

OpenGL.ErrorCode err = GL.GetError();
                Console.WriteLine(err + "  " + OpenTK.Graphics.Glu.ErrorString((OpenTK.Graphics.GluErrorCode)err));
                Console.WriteLine("GL error code: {0}", err);

OpenTK throws an exception when there is an error.

To get the errors and warnings from the shader you could do something like this

            GL.ShaderSource(_fragmenShadertObject, data);
            GL.CompileShader(_fragmenShadertObject);
            GL.GetShaderInfoLog(_fragmenShadertObject, out info);
            GL.GetShader(_fragmenShadertObject, ShaderParameter.CompileStatus, out statusCode);
 
            if (statusCode != 1)
            {
                throw new ApplicationException(info);
            }
            else
            {
                Console.WriteLine(info);
            }
sanchiski's picture

WOW WOW WOW, excellent, the entire problem was the rgba issue on the shader, plus thanks for all the annotations about the code, very helpful :)

jrwatts's picture
Snorkel wrote:

- also (could be wrong here) I don't think you need this

OpenGL.ErrorCode err = GL.GetError();
                Console.WriteLine(err + "  " + OpenTK.Graphics.Glu.ErrorString((OpenTK.Graphics.GluErrorCode)err));
                Console.WriteLine("GL error code: {0}", err);

OpenTK throws an exception when there is an error.

That's wrong, OpenTK generally does not throw exceptions for OpenGL errors.

Snorkel's picture

I check the opentk source now and I still believe it does but only for debug target.
If you look in GL.cs at the beginnen of each function you can see using (new ErrorHelper(GraphicsContext.CurrentContext))

And in ErrorHelper.cs

    // Used in debug-mode only, for automatic OpenGL error-checking.
    //
    // Works like this: an instance is created before each OpenGL function is called.
    // The constructor resets the OpenGL error state. Once the native function returns,
    // the error state is checked again, raising the relevant exceptions.