如何在openGL中扭曲纹理? (透视校正?)

Posted

技术标签:

【中文标题】如何在openGL中扭曲纹理? (透视校正?)【英文标题】:how to warp texture in openGL? (Perspective correction?) 【发布时间】:2020-02-26 16:05:48 【问题描述】:

我想让 OpenGL 程序可以呈现图像并扭曲呈现的图像。

虽然我使用 OpenGL 实现了图像渲染,但我不知道如何扭曲图像。

我想要的一个可变形示例是 (Reference):

但我得到的一张照片是:

据我所知,这个问题与透视校正映射有关。

但我不太了解。

这是我的源代码。

void imageRender(Shader initShader, Shader imgShader, char *path)

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);

float positions =  0.5f, 1.0f, 0.0f, 
                    1.0f, -1.0f, 0.0f, 
                    -1.0f, -1.0f, 0.0f, 
                    -1.0f, 1.0f, 0.0f ;

float vertices[] = 
    // positions                                 // colors           // texture coords
    position[0], position[1], position[2],   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // top right
    position[3], position[4], position[5],   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // bottom right
    position[6], position[7], position[8],   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // bottom left
    position[9], position[10],position[11],  1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // top left 
;
unsigned int indices[] = 
    0, 1, 3,
    1, 2, 3
;

unsigned int VAO, VBO, EBO;

glGenVertexArrays(1, &VAO);
glBindVertexArray(VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

// position attribute 
//glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0); 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);

// color attribute 
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(2);

//texture attribute 
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(3);


FREE_IMAGE_FORMAT format = FreeImage_GetFileType(path, 0);
if (format == -1)
    cerr << BOLDRED << "[ERROR] IMAGE_NOT_FOUND" << RESET << endl;
    exit(1);


if (format == FIF_UNKNOWN)
    cerr << BOLDRED << "[ERROR] UNKNOWN_IMAGE_FORMAT" << RESET << endl;
    format = FreeImage_GetFIFFromFilename(path);
    if (!FreeImage_FIFSupportsReading(format))
        cerr << BOLDRED << "[ERROR] IMAGE_FORMAT_NOT_READABLE" << RESET << endl;
        exit(1);
    


FIBITMAP *bitmap = FreeImage_Load(format, path);
FIBITMAP *bitmap32;

int bitsPerPixel = FreeImage_GetBPP(bitmap);
bitmap32 = FreeImage_ConvertTo32Bits(bitmap);
int imageWidth = FreeImage_GetWidth(bitmap32);
int imageHeight = FreeImage_GetHeight(bitmap32);

GLubyte *textureData = FreeImage_GetBits(bitmap32);

GLuint texture1;
glGenTextures(1, &texture1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, textureData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   // set texture wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

initShader.use();
glBindVertexArray(VAO);

int initalTime = time(NULL);

while(1)

    glBindVertexArray(VAO);

    int timecal = time(NULL);
    //glDrawElements(GL_TRIANGLE_STRIP, 6, GL_UNSIGNED_INT, 0);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glfwSwapBuffers(window);
    glfwPollEvents();
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    if ((timecal - initalTime) > imageTime) // imageTime value is 10 
        break;


glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

glDeleteTextures(1, &texture1);// image
glDisable(GL_TEXTURE_2D);//image

FreeImage_Unload(bitmap32);
FreeImage_Unload(bitmap);

glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);



着色器代码是这样的

//shader.vs
#version 330 core 
layout(location = 1) in vec3 position;
layout(location = 2) in vec3 color;
layout(location = 3) in vec2 texcoord;

out vec3 Color;
out vec2 Texcoord;

void main()

    gl_Position = vec4(position, 1.0);
    Texcoord = texcoord; 


//shader.fs
#version 330 core 
in vec3 Color;
in vec2 Texcoord;

out vec4 outColor;
uniform sampler2D texture5;

void main()

    outColor = texture2D(texture5, Texcoord); 

如何扭曲纹理?

然后使用位置值来扭曲纹理图像是否正确?

【问题讨论】:

【参考方案1】:

这个问题与透视投影无关。您绘制了一个具有 4 个顶点的多边形,平行于视图的 XY 平面,但多边形不是四边形!更改第一个顶点的 x 坐标 (0.5f -> 1.0f)。透视投影适用于Homogeneous coordinates。

一般来说,透视投影是通过Perspective projection 矩阵实现的。当然,您可以定义同质顶点来检查行为:

定义一个具有同质顶点(4个组件)的属性元组:

float vertices[] = 
    // positions                // colors           // texture coords
    1.0f,  1.0f,  0.5f, 2.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f, // top right
    1.0f, -1.0f, -0.5f, 1.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f, // bottom right
   -1.0f, -1.0f, -0.5f, 1.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f, // bottom left
   -1.0f,  1.0f,  0.5f, 2.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f  // top left 
;

调整顶点规范和顶点着色器:

// position attribute 
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)0);
glEnableVertexAttribArray(1);

// color attribute 
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(4 * sizeof(float)));
glEnableVertexAttribArray(2);

//texture attribute 
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (void*)(7 * sizeof(float)));
glEnableVertexAttribArray(3);
#version 330 core 
layout(location = 1) in vec4 position;
layout(location = 2) in vec3 color;
layout(location = 3) in vec2 texcoord;

out vec3 Color;
out vec2 Texcoord;

void main()

    gl_Position = position;
    Texcoord = texcoord; 


实现该效果的另一个选项是,对几何图形添加一个 Z 分量。例如:

float positions =  1.0f,  1.0f,  0.5f, 
                    1.0f, -1.0f,  0.0f, 
                   -1.0f, -1.0f, -0.5f, 
                   -1.0f,  1.0f,  0.0f ;

并根据顶点着色器中的z 计算w 组件(例如w = z + 2.5

#version 330 core 
layout(location = 1) in vec3 position;
layout(location = 2) in vec3 color;
layout(location = 3) in vec2 texcoord;

out vec3 Color;
out vec2 Texcoord;

void main()

    gl_Position = vec4(position, position.z + 2.5);
    Texcoord = texcoord; 

【讨论】:

感谢您的解决方案。但我的解释可能不清楚。我想要的目标就是link。我想为 OpenGL 程序做一个示例函数。请让我知道如何在没有皱纹效果的情况下扭曲 4 个顶点? @YangSsong 抱歉,这太宽泛了。 *** 不是教程。 Stackoveflow 可以帮助您解决特定问题。我建议阅读LeranOpenGL - Camera。 好的.. 谢谢。但我读了你的参考链接。该链接与纹理扭曲有关吗? @YangSsong 你的纹理包装工作。但是你有一个基本的误解。你必须学习透视投影。 (相机) 请注意,还有投影纹理映射,纹理坐标也可以是同质的 4D 坐标,还有 textureProj 函数族可以为您进行透视划分(并且可能直接发生在TMU 而不是着色器)。此处寻求的效果可以通过透视扭曲四边形(直接通过顶点数据中的齐次坐标或通过投影或单应矩阵)或通过投影纹理映射补偿失真映射来实现。

以上是关于如何在openGL中扭曲纹理? (透视校正?)的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 上,如何将桌面捕获到 OpenGL 纹理?

如何将布局扭曲到任何四边形?

透视校正插值

如何构建透视投影矩阵(无 API)

OpenGL - 透视问题

OpenGL 纹理未正确映射