applejacks01
applejacks01

Reputation: 259

C#/OpenTK - Problems applying shaders to loaded texture. Texture is scaled down and flipped

I've been trying to figure out how to get this to work. I'm using OpenTK for C# to modify images. Right now I'm able to load an image successfully, but when I try to modify the image with shaders is where I begin to have problems. I have 3 basic methods:

So here are the 2 problems I'm having:

This is the image I've successfully loaded as a texture:Loaded Texture

Here's the code for LoadTexture:

      public int LoadTexture(string file)
    {
        Bitmap bitmap = new Bitmap(file);

        int tex;
        GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);

        GL.GenTextures(1, out tex);
        GL.BindTexture(TextureTarget.Texture2D, tex);

        BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
       ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
        OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
        bitmap.UnlockBits(data);


        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.Repeat);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);

        return tex;
    }

Here's the code for DrawImage:

        public static void DrawImage(int image)
    {


        GL.MatrixMode(MatrixMode.Projection);
        GL.PushMatrix();
        GL.LoadIdentity();

        GL.Ortho(0, 1920, 0, 1080, -1, 1);

        GL.MatrixMode(MatrixMode.Modelview);
        GL.PushMatrix();
        GL.LoadIdentity();

        GL.Disable(EnableCap.Lighting);

        GL.Enable(EnableCap.Texture2D);

        GL.ActiveTexture(TextureUnit.Texture0);
        GL.BindTexture(TextureTarget.Texture2D, image);

        GL.Begin(PrimitiveType.Quads);

        GL.TexCoord2(0,1);
        GL.Vertex3(0, 0, 0);

        GL.TexCoord2(1, 1);
        GL.Vertex3(1920, 0, 0);

        GL.TexCoord2(1, 0);
        GL.Vertex3(1920, 1080, 0);

        GL.TexCoord2(0, 0);
        GL.Vertex3(0, 1080, 0);


        GL.End();

        AddShaders();

        GL.Disable(EnableCap.Texture2D);
        GL.PopMatrix();

        GL.MatrixMode(MatrixMode.Projection);
        GL.PopMatrix();

        GL.MatrixMode(MatrixMode.Modelview);

        ErrorCode ec = GL.GetError();
        if (ec != 0)
            System.Console.WriteLine(ec.ToString());
        Console.Read();

    }

And here's the code for AddShaders:

      private static void AddShaders()
    {

        /***********Vert Shader********************/
        var vertShader = GL.CreateShader(ShaderType.VertexShader);
        GL.ShaderSource(vertShader, @"attribute vec3 a_position;
                                        varying vec2 vTexCoord;
                                        void main() {
                                        vTexCoord = a_position.xy;
                                        gl_Position = vec4(a_position, 1);
                                        }");
        GL.CompileShader(vertShader);


        /***********Frag Shader ****************/
        var fragShader = GL.CreateShader(ShaderType.FragmentShader);
        GL.ShaderSource(fragShader, @"uniform sampler2D sTexture;
                                      varying vec2 vTexCoord;
                                void main ()
                                {
                                    vec4    color   = texture2D (sTexture, vTexCoord);
                                    color.r = 0.5;
                                    // Save the result
                                    gl_FragColor    = color;
                                }");
        GL.CompileShader(fragShader);

        var program = GL.CreateProgram();
        GL.AttachShader(program, vertShader);
        GL.AttachShader(program, fragShader);
        GL.LinkProgram(program);
        GL.ClearColor(Color.AliceBlue);

        // OpenGL expects vertices to be defined counter clockwise by default
        float[] vertices = {
                // Left bottom triangle
                -1f, 1f, 0f,
                -1f, -1f, 0f,
                1f, -1, 0f,
                // Right top triangle
                1f, -1f, 0f,
                1f, 1f, 0f,
                -1f, 1f, 0f
        };

        var buffer = GL.GenBuffer();
        var positionLocation = GL.GetAttribLocation(program, "a_position");
        GL.EnableVertexAttribArray(positionLocation);
        GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
        GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float,false,0,0);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(ushort)), vertices, BufferUsageHint.StaticDraw);

        GL.DrawArrays(PrimitiveType.Triangles, 0, vertices.Length);
        GL.UseProgram(program);

    }  

I've researched this for a few days and I'm just completely stuck. Thanks to anyone who can see what I'm doing wrong. It HAS to be something small and dumb on my part!

EDIT: When I remove ALL vertex related code in AddShaders, I get the output I want, except its 1/4 the size and flipped in the top right of the screen. So, somehow my shaders don't even care about the vertices. Why is it being scaled down to 1/4 size and flipped?

EDIT2: Ok so thanks to Robert Rouhani, I've ALMOST got this working! Progress It looks like the triangle vertices might be messed up??

Here's my new code. I refactored functionality into methods, stopped creating programs / buffers every frame, etc. Now I have class level variables to hold the GL specific data, methods to create the GL program for the app, create the shaders, create the buffers, etc. Also I know that the 1920x1080 hardcode is, well, hardcoded. That is on my plate to make dynamic.

    string file = "lambo2.png";
    int program;
    int vertShader;
    int fragShader;
    int buffer;
    int positionLocation;
    int texture;
    float[] vertices = {
                // Left bottom triangle
                -1f, -1f, 0f,
                1f, -1f, 0f,
                1f, 1f, 0f,
                // Right top triangle
                1f, 1f, 0f,
                  -1f, 1f, 0f,
                 -1f, -1f, 0f
        };


    private void CreateProgram()
    {
        program = GL.CreateProgram();
        GL.AttachShader(program, vertShader);
        GL.AttachShader(program, fragShader);
        GL.LinkProgram(program);
    }
    private void CreateShaders()
    {
        /***********Vert Shader********************/
        vertShader = GL.CreateShader(ShaderType.VertexShader);
        GL.ShaderSource(vertShader, @"attribute vec3 a_position;
                                        varying vec2 vTexCoord;
                                        void main() {
                                        vTexCoord = (a_position.xy + 1) / 2;
                                        gl_Position = vec4(a_position, 1);
                                        }");
        GL.CompileShader(vertShader);


        /***********Frag Shader ****************/
        fragShader = GL.CreateShader(ShaderType.FragmentShader);
        GL.ShaderSource(fragShader, @"precision highp float;
        uniform sampler2D sTexture;
                                       varying vec2 vTexCoord;
                                 void main ()
                                 {
                                     vec4    color   = texture2D (sTexture, vTexCoord);
                                     if(color.r < 0.3){color.r = 1.0;}
                                     // Save the result
                                     gl_FragColor    = color;
                                 }");
        //   GL.ShaderSource(fragShader, System.IO.File.ReadAllText(@"C:\Users\Matt\Desktop\hue-shader-backup.ps"));
        GL.CompileShader(fragShader);
    }
    private void InitBuffers()
    {
        buffer = GL.GenBuffer();
        positionLocation = GL.GetAttribLocation(program, "a_position");
        GL.EnableVertexAttribArray(positionLocation);
        GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
        GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(ushort)), vertices, BufferUsageHint.StaticDraw);
        GL.VertexAttribPointer(positionLocation, 3, VertexAttribPointerType.Float, false, 0, 0);
    }
    private void Init()
    {
        texture = LoadTexture(file);
        CreateShaders();
        CreateProgram();
        InitBuffers();
    }
    public int LoadTexture(string file)
    {
        Bitmap bitmap = new Bitmap(file);

        int tex;
        GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);

        GL.GenTextures(1, out tex);
        GL.BindTexture(TextureTarget.Texture2D, tex);
        bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
        BitmapData data = bitmap.LockBits(new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
       ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
        OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, data.Scan0);
        bitmap.UnlockBits(data);


        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);

        return tex;
    }

    public void DrawImage(int image)
    {

        GL.Viewport(new Rectangle(0, 0, 1920, 1080));
        GL.MatrixMode(MatrixMode.Projection);
        GL.PushMatrix();
        GL.LoadIdentity();


        GL.Ortho(0, 1920, 0, 1080, 0, 1);

        GL.MatrixMode(MatrixMode.Modelview);
        GL.PushMatrix();
        GL.LoadIdentity();

        GL.Disable(EnableCap.Lighting);

        GL.Enable(EnableCap.Texture2D);

        GL.ActiveTexture(TextureUnit.Texture0);
        GL.BindTexture(TextureTarget.Texture2D, image);

        GL.Begin(PrimitiveType.Quads);

        GL.TexCoord2(0, 1);
        GL.Vertex3(0, 0, 0);

        GL.TexCoord2(1, 1);
        GL.Vertex3(1920, 0, 0);

        GL.TexCoord2(1, 0);
        GL.Vertex3(1920, 1080, 0);

        GL.TexCoord2(0, 0);
        GL.Vertex3(0, 1080, 0);

        GL.End();
        RunShaders();





        GL.Disable(EnableCap.Texture2D);
        GL.PopMatrix();

        GL.MatrixMode(MatrixMode.Projection);
        GL.PopMatrix();

        GL.MatrixMode(MatrixMode.Modelview);


        ErrorCode ec = GL.GetError();
        if (ec != 0)
            System.Console.WriteLine(ec.ToString());
        Console.Read();
    }
    private void RunShaders()
    {

        GL.ClearColor(Color.AliceBlue);
        GL.UseProgram(program);
        GL.DrawArrays(PrimitiveType.Triangles, 0, vertices.Length / 3);

        ErrorCode ec = GL.GetError();
        if (ec != 0)
            System.Console.WriteLine(ec.ToString());
        Console.Read();

    }

Upvotes: 0

Views: 1228

Answers (1)

Robert Rouhani
Robert Rouhani

Reputation: 14678

Going to start an answer instead of continuing the comments. You've still got a few minor issues that are compounding. You should comment out everything between and including GL.Begin and GL.End as the RunShaders function should do draw everything you need to the screen. Also comment out the GL.Ortho line, you don't need it if you're working with the vertices in the [-1, 1] range.

Second, your issue is that you're only uploading half your vertex buffer to the GPU. In InitBuffers on the GL.BufferData line, change sizeof(ushort) to sizeof(float), since your vertices are floats (4 bytes long) and not ushorts (2 bytes long).

GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vertices.Length * sizeof(float)), vertices, BufferUsageHint.StaticDraw);

Together this should get your program working.

Upvotes: 1

Related Questions