Как разместить изображение (текстуру) внутри четырехугольника в 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);
}

person wEight    schedule 14.05.2021    source источник


Ответы (1)


Все, что вам нужно сделать, это указать координаты текстуры в диапазоне [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 (strid) определяет смещение в байтах между последовательными общими атрибутами вершин. Поскольку каждый атрибут состоит из 4 элементов типа flaot (x, y, u, v), это 4*sizeof(float). Последний аргумент — это смещение атрибута в байтах. Смещение координат вершин равно 0, а смещение координат текстуры равно 2*sizeof(float).


Конечно, вы можете использовать 2 отдельных массива атрибутов. В этом случае шаг равен 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));
person Rabbid76    schedule 14.05.2021
comment
Это решило мою проблему, спасибо! Однако можете ли вы использовать отдельный массив для координат текса в том же буфере вершин, если я планирую обновлять только атрибут позиции? Я пробовал это раньше и в конечном итоге столкнулся с конфликтом атрибутов position и uv (tex coords). - person wEight; 14.05.2021
comment
Всего один объект буфера вершин. - person wEight; 14.05.2021
comment
Это все еще один массив. Я спрашивал о двух отдельных массивах. Один массив для позиции, другой для UV. например. List<float> vertex_pos = {...}; List<float> vertex_uv = {...}; - person wEight; 14.05.2021
comment
Да, точно. Один ВБО. - person wEight; 14.05.2021
comment
@wEight Я расширил ответ. - person Rabbid76; 14.05.2021