如何在opengl中正确升级纹理?

Posted

技术标签:

【中文标题】如何在opengl中正确升级纹理?【英文标题】:How to properly upscale a texture in opengl? 【发布时间】:2018-12-30 00:15:39 【问题描述】:

假设,为了这个问题的简单性,我想创建一个包含从黑色到红色的颜色渐变的纹理。我知道有很多更简单的方法可以实现这一点,但这是解决我的问题的一个很好的例子。

我想,我可以像这样简单地从浮点数组创建纹理:

float values[4] 
    0.f, 1.f,
    0.f, 1.f


// gen texture
unsigned int texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);

// set filter and wrap
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

// copy values
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 2, 2, 0, GL_RED, GL_FLOAT, values);

然后画出来:

float vertices[8] 
    -1.f, -1.f, 0.f, 0.f,
     1.f, -1.f, 1.f, 0.f,
     1.f,  1.f, 1.f, 1.f,
    -1.f,  1.f, 0.f, 1.f


// generate vertex buffer, vertex buffer layout and vertex array

unsigned int[6] indices 
    0, 1, 2,
    2, 3, 1


// generate index buffer

// bind everything

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);

绑定的顶点着色器如下所示:

#version 330 core

layout(location=0) in vec2 in_position;
layout(location=1) in vec2 in_texCoords;

out vec2 texCoords;

void main() 
    gl_Position = vec4(in_position, 0.f, 1.f);
    texCoords = in_texCoords;

片段着色器是这样的:

#version 330 core

layout(location = 0) out vec4 out_color;

in vec2 texCoords;

uniform sampler2D u_gradient;

void main() 
    float red = texture(u_gradient, texCoords).r;
    out_color = vec4(red, 0.f, 0.f, 1.f);

在此之后,我希望在整个窗口中获得从左到右(从黑色到红色)的颜色渐变。 然而,我得到的是在 x 和 y 方向上从窗口的大约 1/4 到 3/4 的渐变(见下图)。我还注意到重复的包装在这里似乎并不适用,因为我得到的看起来像镜像重复。

结果:

我使用固定值而不是 texCoords 使用片段着色器,并发现 x 轴上从 0.25 到 0.75 的范围代表整个渐变(红色为 0.25 映射到 0,红色为 0.75 到 1 )。

更改作为值传递给纹理(例如 4x4 数组)的“步数”数量不会改变结果。

我也尝试过使用图像作为纹理(加载 stb_image,分辨率 1920x1080),效果非常好,并且遍布整个屏幕。

澄清一下:为什么渐变的 texCoords 范围从 0.25 到 .75 而不是从 0 到 1,我该如何修复它(除了调整 texCoords 本身)?

【问题讨论】:

请看Textures objects and parameters,特别是GL_REPEAT的效果。请改用GL_CLAMP_TO_EDGE。 (顺便说一句。你设置了两次GL_TEXTURE_WRAP_S - 第二次可能是GL_TEXTURE_WRAP_T。) 复制粘贴代码时出现换行错误。它在我的代码中是正确的,我也在这里修复了它。 @Scheff 【参考方案1】:

假设你有一个 2x2 的纹理

2x2 纹理

如果此纹理被包裹在 6x6 片段的网格上,则纹素的中心正好在 6x6 正方形的 3x3 平铺中间的纹素上:

6x6 四边形

其他片段的颜色取决于纹理参数 - 请参阅glTexParameter

由于纹理被放大,GL_TEXTURE_MAG_FILTER 很重要。

如果是GL_NEAREST,那么片段的颜色是最接近片段纹理坐标的纹素之一:

如果是GL_LINEAR,则颜色被插值,通过最接近纹理坐标的4个像素的加权平均值。

6x6 四边形边界处的插值取决于环绕参数GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_T

如果参数为GL_REPEAT(默认),则纹理被视为无限纹理,边界颜色插值的插值考虑了纹理对面的纹素。这用于无缝纹理和平铺:

如果是GL_CLAMP_TO_EDGE,那么边界处的插值颜色被钳制为纹理边界处纹素的颜色:


将纹理包裹参数GL_CLAMP_TO_EDGE应用于纹理对象,

float values[4]  0.f, 1.f, 0.f, 1.f ;

unsigned int texId;
glGenTextures(1, &texId);
glBindTexture(GL_TEXTURE_2D, texId);

// set filter and wrap
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

// copy values
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 2, 2, 0, GL_RED, GL_FLOAT, values);

得到以下渐变:

【讨论】:

现在从您的回答中并不清楚,所以我认为您应该添加一些说明,即纹素值是为纹素网格中每个“单元格”的中心定义的,以及坐标0 和 1 不在纹素中心。也许还可以对 OP 的原始代码进行修改,以正确处理纹素中心并显示结果。

以上是关于如何在opengl中正确升级纹理?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用OpenGL着色器在平面上绘制没有透视的纹理?

如何使用 FreeImage 库将纹理加载到 OpenGL?

如何在OpenGL中反转纹理颜色

通过表面写入 CUDA 中的浮点 OpenGL 纹理

如何在OpenGL中处理纹理动画? [关闭]

如何使用 OpenGL 在 SDL 中获得正确的 SourceOver alpha 合成