如何在 OpenTK 中将图像(纹理)放入四边形内?

How to fit an image (texture) inside a quad in OpenTK?

我一直在尝试研究 OpenTK 中纹理的不同示例,但是,几乎没有代码示例使用与我希望的相同的方法,或者需要大量不符合我需求的毫无意义的解决方法。我只是想在 OpenTK 中绘制图像,而不会使它们的 UV 变形或变形。或者更确切地说,我如何对它们进行畸形处理以适应原语(在本例中为 quad/square),无论它位于 2D 世界中的哪个位置?

考虑这张图片(这是我试图放入四边形基元中的纹理):

这是不想要的结果。如您所见,它被裁剪了。我不关心包装,因为我计划将整个图像安装在正方形内(不需要纵横比)。不同的包装设置没有任何作用。图像的中心仍在正方形之外。

透明度和调色板是我担心的事情,我只需要帮助使整个图像适合正方形!

这是我加载纹理的代码:

    public Texture(Bitmap image)
    {
        ID = GL.GenTexture();

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

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

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

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

        GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
    }

然后这是我将顶点数据加载到着色器并绘制图元的代码:

        List<float> vertex_data = { 

            -.25f, -.25f,
            -.25f, .25f, 
            .25f, .25f,
            .25f, -.25f
        };

        // Load the 2D object
        GL.UseProgram(program);

        GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
        GL.BufferData(BufferTarget.ArrayBuffer, vertex_data.Count * sizeof(float), vertex_data.ToArray(), BufferUsageHint.DynamicDraw);
        
        GL.EnableVertexAttribArray(attr_pos);
        GL.VertexAttribPointer(attr_pos, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), 0);
        GL.EnableVertexAttribArray(attr_uv);
        GL.VertexAttribPointer(attr_uv, 2, VertexAttribPointerType.Float, false, 2 * sizeof(float), 0);
        
        // ^^ Using the same position data for the UV as well.
        
        // ...
        
        // Drawing the 2D object
        GL.UseProgram(program);

        GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);

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

        GL.DrawArrays(PrimitiveType.Quads, 0, 4);

以及顶点和片段着色器:

垂直:

#version 330 core

layout(location = 1) in vec3 vertex_pos;
layout(location = 2) in vec2 vertex_uv;

out vec2 uv;

uniform mat4 mvp;

void main() {

    gl_Position = mvp * vec4(vertex_pos, 1);
    uv = vertex_uv;
}

片段:

#version 330 core

in vec2 uv;

out vec4 color;

uniform sampler2D texture0;

void main() {
    
    color = texture(texture0, uv);
}

您需要做的就是在 [0.0, 1.0] 范围内指定纹理坐标:

List<float> vertex_data = { 

//   x      y     u     v 
    -.25f, -.25f, 0.0f, 0.0f,
    -.25f,  .25f, 0.0f, 1.0f,
     .25f,  .25f, 1.0f, 1.0f,
     .25f, -.25f, 1.0f, 0.0f,
};

// Load the 2D object
GL.UseProgram(program);

GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer, vertex_data.Count * sizeof(float),
    vertex_data.ToArray(), BufferUsageHint.DynamicDraw);

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

纹理坐标 (0, 0) 指向纹理的左下边缘,纹理坐标 (1, 1) 指向纹理的右上边缘。
您必须将纹理坐标 (0, 0) 关联到四边形的左下角,并将纹理坐标 (1, 1) 关联到四边形的右上角。

GL.VertexAttribPointe 的第 4 个参数 (strid) 指定连续通用顶点属性之间的字节偏移量。由于每个属性由 flaot (x, y, u, v) 类型的 4 个元素组成,因此是 4*sizeof(float)。最后一个参数是属性的字节偏移量。顶点坐标的偏移量为0,纹理坐标的偏移量为2*sizeof(float)


当然你可以用2个分开的属性数组。在这种情况下,stride对于顶点坐标和纹理坐标来说是2*sizeof(float)。顶点坐标的偏移量为0,纹理坐标的偏移量为所有顶点坐标的大小(8*sizeof(float)):

使用GL.BufferSubData初始化缓冲区数据:

List<float> vertex_data = { 
    
//   x      y  
    -.25f, -.25f, 
    -.25f,  .25f,
     .25f,  .25f,
     .25f, -.25f,
}

List<float> texture_data = { 
//   u      v  
     0.0f, 0.0f,
     0.0f, 1.0f,
     1.0f, 1.0f,
     1.0f, 0.0f,
};

// Load the 2D object
GL.UseProgram(program);

GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
GL.BufferData(BufferTarget.ArrayBuffer, 
    vertex_data.Count * sizeof(float) + texture_data.Count * sizeof(float), 
    IntPtr.Zero, BufferUsageHint.DynamicDraw);
GL.BufferSubData(BufferTarget.ArrayBuffer, 0, 
    vertex_data.Count * sizeof(float), vertex_data.ToArray())
GL.BufferSubData(BufferTarget.ArrayBuffer, vertex_data.Count * sizeof(float), 
    texture_data.Count * sizeof(float), texture_data.ToArray())

GL.EnableVertexAttribArray(attr_pos);
GL.VertexAttribPointer(attr_pos, 2, VertexAttribPointerType.Float, false, 
    2 * sizeof(float), 0);
GL.EnableVertexAttribArray(attr_uv);
GL.VertexAttribPointer(attr_uv, 2, VertexAttribPointerType.Float, false, 
    2 * sizeof(float), vertex_data.Count * sizeof(float));