初学者在OpenGL中使用2D投影和纹理

时间:2014-03-31 21:53:47

标签: c# opengl opentk

最近我一直在努力学习/使用OpenGL 3+。我已经查看了教程和示例,但是我遇到了试图让纹理和2D投影无问题地工作的墙。

现在的目标是拥有一个可以在屏幕上绘制纹理四边形的功能,其位置由像素(不是[-1,1])指定。

为了便于阅读和测试,我使用我目前拥有的知识制作了一个新的准系统程序,它表现出几乎相同的问题。我将不胜感激,因为我开始对此赞不绝口:( ..

当前代码显示乱码纹理而不是图像本身(纹理为128x128px)。

enter image description here

[Program.cs中]

namespace OpenGLTester
{
    static class Program
    {
        public static GameWindow window;
        public static String programDirectory = Directory.GetCurrentDirectory();
        public static int testTexture;
        public static int uniform_fragment_texture;
        public static int shaderProgram;

        [STAThread]
        static void Main()
        {
            window = new GameWindow(1024, 768, new GraphicsMode(new ColorFormat(8, 8, 8, 8), 0, 8), "OpenGLTester", GameWindowFlags.Default, DisplayDevice.Default, 3, 1, GraphicsContextFlags.Default);
            GL.Viewport(new Size(1024,768));

            shaderProgram = GL.CreateProgram();
            int vertexShader = GL.CreateShader(ShaderType.VertexShader);
            int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);

            GL.ShaderSource(vertexShader, File.ReadAllText(programDirectory + @"\vertex.vert"));
            GL.ShaderSource(fragmentShader, File.ReadAllText(programDirectory + @"\fragment.frag"));

            GL.CompileShader(vertexShader);
            GL.CompileShader(fragmentShader);

            GL.AttachShader(shaderProgram, vertexShader);
            GL.AttachShader(shaderProgram, fragmentShader);
            GL.LinkProgram(shaderProgram);

            if (GL.GetError() != ErrorCode.NoError) { System.Diagnostics.Debugger.Break(); }

            Console.WriteLine(GL.GetProgramInfoLog(shaderProgram));

            GL.UseProgram(shaderProgram);

            Matrix4 projectionMatrix = Matrix4.CreateOrthographic(1024, 768, 0, 1);

            GL.UniformMatrix4(GL.GetUniformLocation(shaderProgram, "vertex_projection"), false, ref projectionMatrix);

            uniform_fragment_texture = GL.GetUniformLocation(shaderProgram, "fragment_texture");

            testTexture = loadTexture(programDirectory + @"\test.png");

            GL.Disable(EnableCap.DepthTest);
            GL.Disable(EnableCap.Lighting);
            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactorSrc.One, BlendingFactorDest.OneMinusSrcAlpha);

            window.UpdateFrame += window_UpdateFrame;
            window.RenderFrame += window_RenderFrame;
            window.Resize += window_Resize;

            window.TargetRenderFrequency = 60;
            window.Run();
        }

        static void window_Resize(object sender, EventArgs e)
        {
            //Don't allow resizing for now.
            window.Size = new Size(1024, 768);
        }

        static void window_UpdateFrame(object sender, FrameEventArgs e)
        {
            ErrorCode currentError = GL.GetError();
            if (currentError != ErrorCode.NoError)
            {
                Console.WriteLine(Enum.GetName(typeof(ErrorCode), currentError));
                System.Diagnostics.Debugger.Break();
            }
        }

        static void window_RenderFrame(object sender, FrameEventArgs e)
        {
            GL.ClearColor(0, 0, 0, 0);
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.StencilBufferBit);

            //test texture is 128x128pixels.
            drawTexRect(100, 228, 100, 228, testTexture);

            window.SwapBuffers();
        }

        static int loadTexture(String filePath)
        {
            GL.Enable(EnableCap.Texture2D);
            int id = GL.GenTexture();

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

            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureBaseLevel, 0);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMaxLevel, 0);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

            Bitmap bmp = new Bitmap(filePath);
            BitmapData bmp_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

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

            bmp.UnlockBits(bmp_data);
            bmp.Dispose();

            return id;
        }

        static void drawTexRect(float top, float bottom, float left, float right, int texture)
        {
            //topLeft,bottomLeft,bottomRight,topRight

            float[] vertices = new float[] {
               left, top, 0, 0,
               left, bottom, 0, 1,
               right, bottom, 1, 1,
               right, top, 1, 0,
            };

            int buffer = GL.GenBuffer();

            GL.BindBuffer(BufferTarget.ArrayBuffer, buffer);
            GL.BufferData<float>(BufferTarget.ArrayBuffer, new IntPtr(vertices.Length * sizeof(float)), vertices, BufferUsageHint.StaticDraw);

            //vec2 - screen position
            GL.EnableVertexAttribArray(0);
            GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4, 0);

            //vec2 - texture coordinates
            GL.EnableVertexAttribArray(1);
            GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4, 2 * sizeof(float));

            GL.Enable(EnableCap.Texture2D);
            GL.ActiveTexture(TextureUnit.Texture0);
            GL.BindTexture(TextureTarget.Texture2D, texture);
            GL.Uniform1(uniform_fragment_texture, 0);

            GL.DrawArrays(PrimitiveType.Quads, 0, 4);
            GL.DeleteBuffer(buffer);
        }
    }
}

[vertex.vert]

#version 330

in vec2 vertex_position;
in vec2 vertex_texturePosition;

uniform mat4 vertex_projection;

out vec2 fragment_texturePosition;

void main()
{
    gl_Position = vec4(vertex_position,0.0,1.0) * vertex_projection;

    fragment_texturePosition = vertex_texturePosition;
}

[fragment.frag]

#version 330

in vec2 fragment_texturePosition;

uniform sampler2D fragment_texture;

out vec4 output_color;

void main()
{
    output_color = texture(fragment_texture,fragment_texturePosition);
}

@ j-p建议的更改之后仍然存在一个问题:

enter image description here

@ j-p建议的纹理位置变化后:

投影也是错误的,因为我预计它的位置是从左边100像素,从顶部100像素,不知道如何解决这个问题。

1 个答案:

答案 0 :(得分:2)

stride参数以字节为单位:

GL.EnableVertexAttribArray(0);
GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 0);

//vec2 - texture coordinates
GL.EnableVertexAttribArray(1);
GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 2 * sizeof(float));

此外,windows argb位图的相应opengl像素格式为BGRA。 (link

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

最后,您的纹理坐标应调整如下:

        float[] vertices = new float[] {
           left, top, 0, 1,
           left, bottom, 0, 0,
           right, bottom, 1, 0,
           right, top, 1, 1
        };