如何在 openGL 3 中设置 "q" 纹理坐标?

How can I set the "q" texture coordinate in openGL 3?

我试图在 OpenGL 中将方形纹理应用到类似梯形的形状,但出现了一些变形。我已经阅读了很多关于可能的解决方案的文章,而看起来最方便的解决方案需要修改“q”纹理坐标。这是在解决方案中使用 GlTexCoord 函数完成的;但是,我正在使用顶点缓冲区,但我不知道如何使用它们以这种方式更改此坐标。 GLSL中的texture init取的是一个vec2;所以我不知道如何将二维纹理坐标以外的任何东西传递给它。

main.c

//C libs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

//GL libs (Need to have set up to run this)
#include <glad/glad.h>
#include <GLFW/glfw3.h>

//Libs in folder
#include "shader.h"
#include "image.h"

//Window consts
#define WINDOW_HEIGHT 500
#define WINDOW_WIDTH 500
#define WINDOW_NAME "ForWhosebug"

//Shader consts
#define VERTEX_SHADER_FILE "vertex_shader.glsl"
#define FRAGMENT_SHADER_FILE "fragment_shader.glsl"

//Vertex constants
#define POSITION_ATTRIBUTE_LOC 0
#define TEXTURE_COORD_ATTRIBUTE_LOC 1
#define POSITION_SIZE 2
#define TEXTURE_COORD_SIZE 2
#define VERTEX_SIZE (POSITION_SIZE + TEXTURE_COORD_SIZE) //Amount of floats per vertex
#define POSITION_OFFSET 0
#define TEXTURE_COORD_OFFSET (POSITION_SIZE * sizeof(float))
#define STRIDE (sizeof(float) * VERTEX_SIZE)

//Functions
static void framebuffer_size_callback(GLFWwindow*, int, int);
static unsigned int load_bmp_texture(const char* name);

int main() 
{
    printf("Running!\n");

    //*GLFW
    if (!glfwInit())
    {
        printf("GLFW init fail\n");
        return -1;
    }

    //3.3 core
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    //*Window object
    GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_NAME, NULL, NULL);
    if (window == NULL) 
    {
        printf("GLFW window fail\n");
        return -1;
    }

    glfwMakeContextCurrent(window);

    //*GLAD
    if (!gladLoadGLLoader((GLADloadproc) &glfwGetProcAddress))
    {
        printf("GLAD init fail");
        glfwTerminate();
        return -1;
    }
    
    //*Window
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
    glfwSetFramebufferSizeCallback(window, &framebuffer_size_callback);

    //*Shaders
    ShaderProgram shader_program;
    if (!shader_program_init(&shader_program, VERTEX_SHADER_FILE, FRAGMENT_SHADER_FILE)) {
        glfwTerminate();
        return -1;
    }

    //*Triangle rendering
    //Vertices
    float tri_vertices[4 * VERTEX_SIZE] = { //FORM A TRAPEZOID
        //Position       //Texture coordinates
        -0.5f,   0.5f,   0.0f, 1.0f,               //Top-left
        -0.5f,  -0.5f,   0.0f, 0.0f,               //Bottom-left
         0.5f,   0.75f,  1.0f, 1.0f,               //Top-right
         0.5f,  -0.75f,  1.0f, 0.0f                //Bottom-right
    };

    //Indices
    unsigned int tri_indices[6] = {
        2, 0, 1, //Top-right, top-left, bottom-left
        2, 3, 1  //Top-right, bottom-right, bottom-left
    };

    //VAO
    unsigned int tri_vao;
    glGenVertexArrays(1, &tri_vao);
    glBindVertexArray(tri_vao);

    //VBO
    unsigned int tri_vbo;
    glGenBuffers(1, &tri_vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, tri_vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(tri_vertices), tri_vertices, GL_STATIC_DRAW);

    //EBO
    unsigned int tri_ebo;
    glGenBuffers(1, &tri_ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(tri_indices), tri_indices, GL_STATIC_DRAW);
    
    //Config
    //Position
    glVertexAttribPointer(POSITION_ATTRIBUTE_LOC, POSITION_SIZE, GL_FLOAT, GL_FALSE, STRIDE, (void*) POSITION_OFFSET);
    glEnableVertexAttribArray(POSITION_ATTRIBUTE_LOC);

    //Texture coordinates
    glVertexAttribPointer(TEXTURE_COORD_ATTRIBUTE_LOC, TEXTURE_COORD_SIZE, GL_FLOAT, GL_FALSE, STRIDE, (void*) TEXTURE_COORD_OFFSET); 
    glEnableVertexAttribArray(TEXTURE_COORD_ATTRIBUTE_LOC);

    //*Textures
    unsigned int brick_tex = load_bmp_texture("purple_bricks.bmp");

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, brick_tex);

    //Attaching to uniform
    glUseProgram(shader_program);
    glUniform1i(glGetUniformLocation(shader_program, "brick"), 0);

    //*Rendering setup
    //Shader
    glUseProgram(shader_program);

    //*Main loop
    while (!glfwWindowShouldClose(window)) 
    {

        //*Blittering
        //Background
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        //Triangles
        glBindVertexArray(tri_vao);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tri_ebo);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*) 0);

        //*Buffer, events
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    //*End program
    glDeleteVertexArrays(1, &tri_vao);
    glDeleteBuffers(1, &tri_vbo);
    glDeleteBuffers(1, &tri_ebo);
    glfwTerminate();
    return 0;
}

//Centers the OpenGL part of the window and keeps the same width and height
static void framebuffer_size_callback(GLFWwindow* window, int width, int height) {
    glViewport((width - WINDOW_WIDTH) / 2, (height - WINDOW_HEIGHT) / 2, WINDOW_WIDTH, WINDOW_HEIGHT);
} 

//Loads and sets up a BMP texture
static unsigned int load_bmp_texture(const char* name) {

    //Loading into array
    RGBImage image;
    read_bmp(name, &image);

    //Generating texture in GL
    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    //Setting mapping
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); //X 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); //Y

    //Setting filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    //Setting texture and mipmap
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width, image.height, 0, GL_RGB, GL_UNSIGNED_BYTE, image.data);
    glGenerateMipmap(GL_TEXTURE_2D);

    //Freeing image array
    free_RGBImage(image);

    return texture;
}

image.h

//Code for loading a bmp file as an array
//Definitely not part of the problem
//24 bit bmps

//C libs
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>

//Prevent struct packing
#pragma pack(1)

typedef struct RGB {
    unsigned char R;
    unsigned char G;
    unsigned char B;
} RGB;

typedef struct RGBImage {
    int width;
    int height;
    RGB* data;
} RGBImage;

typedef struct BMPFileHeader {
    char name[2];
    uint32_t size;
    uint32_t garbage;
    uint32_t image_offset;
} BMPFileHeader;

typedef struct BMPInfoHeader {
    uint32_t header_size;
    uint32_t width;
    uint32_t height;
    uint16_t color_planes;
    uint16_t bits_per_pixel;
    uint32_t compression;
    uint32_t image_size;
} BMPInfoHeader;

void free_RGBImage(RGBImage image) {
    free(image.data);
}

bool read_bmp(const char* file_name, RGBImage* image) {
    FILE* fp = fopen(file_name, "rb");
    if (fp == NULL) {
        printf("Couldn't open %s\n", file_name);
        return false;
    }

    BMPFileHeader file_header;

    fread(file_header.name, sizeof(BMPFileHeader), 1, fp);

    if ((file_header.name[0] != 'B') || (file_header.name[1] != 'M')) {
        fclose(fp);
        return false;
    }

    BMPInfoHeader info_header;

    fread(&info_header, sizeof(BMPInfoHeader), 1, fp);

    if ((info_header.header_size != 40) || (info_header.compression != 0) || (info_header.bits_per_pixel != 24)) {
        fclose(fp);
        return false;
    }

    fseek(fp, file_header.image_offset, SEEK_SET);

    image->width = info_header.width;
    image->height = info_header.height;

    image->data = (RGB*) malloc(image->height * image->width * sizeof(RGB));

    for (int i = 0; i < image->height; i++) {
        fread(&image->data[i * image->width], sizeof(RGB), image->width, fp);
    }

    int R;
    for (int i = 0; i < image->height * image->width; i++) {
        R = image->data[i].R;
        image->data[i].R = image->data[i].B;
        image->data[i].B = R;
    }

    fclose(fp);

    return true;
}

shader.h

//Code for compiling and linking a shader
//Definitely not part of the problem

//C libs
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

//GL libs
#include <glad/glad.h>
#include <GLFW/glfw3.h>

//Consts
#define INFO_LOG_SIZE 512

static bool _glad_is_init();
static bool _glfw_is_init();

typedef unsigned int ShaderProgram;

bool shader_program_init(ShaderProgram* shader_program, char* vertex_shader_file_name, char* fragment_shader_file_name) 
{
    if (!_glfw_is_init()) {
        printf("Shader: glfw uninitialized\n");
        return 0;
    }

    else if (!_glad_is_init()) {
        printf("Shader: glad uninitialized\n");
        return 0;
    }

    long int file_size;
    size_t new_source_length;
    FILE* fp;

    //*Reading vertex shader file
    //Open
    fp = fopen(vertex_shader_file_name, "r");
    if (fp == NULL) {
        printf("Couldn't open vertex shader file\n");
        return 0;
    }

    //Find length for buffer
    fseek(fp, 0L, SEEK_END);
    file_size = ftell(fp);
    if (file_size == -1) {
        printf("Couldn't seek end of file\n");
        return 0;
    }
    rewind(fp);

    char vertex_shader_source[(file_size + 1) * sizeof(char)];

    //Read
    new_source_length = fread(vertex_shader_source, sizeof(char), file_size, fp);
    if (ferror(fp) != 0) {
        printf("Error when reading file\n");
        return 0;
    }

    //Add string termination
    vertex_shader_source[new_source_length] = '[=13=]';

    //Close
    fclose(fp);
    
    //*Reading fragment shader
    //Open
    fp = fopen(fragment_shader_file_name, "r");
    if (fp == NULL) {
        printf("Couldn't open fragment shader file\n");
        return 0;
    }

    //Find length for buffer
    fseek(fp, 0L, SEEK_END);
    file_size = ftell(fp);
    if (file_size == -1) {
        printf("Couldn't seek end of file\n");
        return 0;
    }
    rewind(fp);
    char fragment_shader_source[(file_size + 1) * sizeof(char)];

    //Read
    new_source_length = fread(fragment_shader_source, sizeof(char), file_size, fp);
    if (ferror(fp) != 0) {
        printf("Error reading file\n");
        return 0;
    }

    //Add string termination
    fragment_shader_source[new_source_length] = '[=13=]';

    //Close
    fclose(fp);

    //*Compiling
    //For error checking
    int success;
    char infolog[INFO_LOG_SIZE];
    
    const char* vertex_shader_code = vertex_shader_source; //vertex_shader_source is of type char foo[n], a VLA.
    const char* fragment_shader_code = fragment_shader_source;
    
    //Vertex
    unsigned int vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertex_shader, 1, &vertex_shader_code, NULL);
    glCompileShader(vertex_shader);

    glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertex_shader, INFO_LOG_SIZE, NULL, infolog);
        printf("Vertex shader compile fail: \n%s\n", infolog);
        return 0;
    }

    //Fragment
    unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragment_shader, 1, &fragment_shader_code, NULL);
    glCompileShader(fragment_shader);

    glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragment_shader, INFO_LOG_SIZE, NULL, infolog);
        printf("Fragment shader compile fail: \n%s\n", infolog);
        return 0;
    }

    //Program
    *shader_program = glCreateProgram();
    glAttachShader(*shader_program, vertex_shader);
    glAttachShader(*shader_program, fragment_shader);
    glLinkProgram(*shader_program);

    glGetProgramiv(*shader_program, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(*shader_program, INFO_LOG_SIZE, NULL, infolog);
        printf("Shader program compile fail: \n%s\n", infolog);
        return 0;
    }

    //Deleting
    glDeleteShader(vertex_shader);
    glDeleteShader(fragment_shader);

    return 1;
}

bool _glfw_is_init() {
    if (!glfwInit())
    {
        return false;
    }
    return true;
}

bool _glad_is_init() {
    if (!gladLoadGLLoader((GLADloadproc) &glfwGetProcAddress))
    {
        return false;
    }
    return true;
}

vertex_shader.glsl

#version 330 core

layout (location = 0) in vec2 vertex_position;
layout (location = 1) in vec2 vertex_texture_coordinate;

out vec2 texture_coordinate;

void main()
{
    gl_Position = vec4(vertex_position, 1.0, 1.0);
    texture_coordinate = vertex_texture_coordinate;
};

fragment_shader.glsl

#version 330 core

in vec4 color;
in vec2 texture_coordinate;

out vec4 FragColor;

uniform sampler2D brick;

void main()
{
    FragColor = texture(brick, texture_coordinate);
};

使用的纹理

程序结果

编辑:对于那些以后阅读的人来说,这个 link 对实施下面答案中描述的方法有很大帮助: https://www.cs.cmu.edu/~16385/s17/Slides/10.2_2D_Alignment__DLT.pdf

给定 2d 顶点坐标 P[i] 和 2d 纹理坐标 T[i],您需要找到 homography that maps from T[i] to P[i]. The homography H is represented with a 3x3 matrix (up to a scaling factor) and can be calculated, for example, with the direct linear transform method。请注意,它涉及求解具有 eight/nine 个未知数的八个方程组——因此最好求助于现有的库,例如 LAPACK。

单应性满足关系

H * T[i] ~ P[i]

作为射影平面上的等价物。即

H * (T[i], 1) = a[i] * (P[i], 1)

对于某些比例因子 a[i]

获得正确纹理映射的技巧是用 3d 同类坐标替换 2d 顶点坐标:

homogeneous P[i] = H * (T[i], 1)

顶点着色器将收到一个vec3顶点位置,并将第3个坐标复制到gl_Position.w:

layout (location = 0) in vec3 vertex_position;
...
void main() {
    gl_Position = vec4(vertex_position.xy, 0, vertex_position.z);
    ...
}

这是我用这种方法得到的结果 (corrected/uncorrected):