首页 > 解决方案 > 宽度不均匀时纹理缩放错误

问题描述

我试图让多个纹理在 GLFW 窗口中逐像素出现。我的做法是我有一个共享着色器,它采用一个统一的矩阵来缩放和转换纹理。但是,似乎我的缩放并不完美,因为纹理看起来已损坏(?)。

不均匀的

这是过滤器设置为 的时候GL_NEAREST。当设置为 时GL_LINEAR,它会变得模糊,我也不想要。glGetError()这让我怀疑这是不适当的缩放,增加了返回的事实GL_NO_ERROR。但这没有任何意义,因为我将其缩放为相对于屏幕像素和位图像素的“像素完美”......(见下面的代码)

通过广泛的研究和测试我能想到的一切,我找到了根本原因:当位图的宽度不均匀时,就会发生损坏。当我在位图不均匀时添加代码以填充位图时......

甚至

腐败消失,纹理完美呈现!(以上测试多)

为什么会这样,我该如何解决?

纹理创建代码(摘录):

...
float vertices[] = {
    // positions   // texture coords
    1.0,   1.0,  1.0f, 1.0f, // top right
    1.0,  -1.0,  1.0f, 0.0f, // bottom right
    -1.0, -1.0,  0.0f, 0.0f, // bottom left
    -1.0,  1.0,  0.0f, 1.0f  // top left 
};

unsigned int indices[] = {
    0, 1, 3,
    1, 2, 3
};

// `VA`, `VB`, `EB`, and `TX` are class variables
glGenVertexArrays(1, &VA);
glGenBuffers(1, &VB);
glGenBuffers(1, &EB);
glGenTextures(1, &TX);

glBindTexture(GL_TEXTURE_2D, TX);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

// Sharp, easier to tell when texture is rendering incorrectly
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// Blurry
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glBindVertexArray(VA);

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

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

constexpr int stride = 4 * sizeof(float);

// position attribute
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, stride, (void*)0);
glEnableVertexAttribArray(0);
// texture coord attribute
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, stride, (void*)(2 * sizeof(float)));
glEnableVertexAttribArray(1);

// `shader` is a shader program for the below shaders
glUseProgram(shader);
glUniform1i(glGetUniformLocation(shader, "Tex"), 0);
...

纹理加载代码,每个纹理调用一次(摘录):

...
unsigned char *bitmap = loadImage(&width, &height,...);
glBindTexture(GL_TEXTURE_2D, TX);
// Before you ask: yes, the image is in RBGA
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, bitmap);
...

纹理渲染代码,每帧调用一次(再次摘录):

...
glm::mat4 model(1.0f);

// Probally not relevent
const float xPos = (x + width / 2) / windowWidth * 2 - 1;
const float yPos = ((y + height / 2) / windowHeight * 2 - 1) * -1;
model = glm::translate(model, glm::vec3(xPos, yPos, 0.0f));

model = glm::scale(model, glm::vec3((float)width / windowWidth, (float)height / windowHeight, 0.0f));

// `modelLocation` was previously set to glGetUniformLocation(shader, "model")
glUniformMatrix4fv(modelLocation, 1, GL_FALSE, glm::value_ptr(model));

glBindVertexArray(VA);
glBindBuffer(GL_ARRAY_BUFFER, VB);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EB);
glBindTexture(GL_TEXTURE_2D, TX);

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
...

顶点着色器:

#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 model;

void main() {
    gl_Position = model * vec4(aPos, 0.0, 1.0);
    TexCoord = aTexCoord;
}

片段着色器:

#version 330 core
out vec4 FragColor;

in vec2 TexCoord;

uniform sampler2D Tex;

void main() {
    FragColor = texture(Tex, TexCoord);
}

标签: c++opengltexturesscaling

解决方案


m当您想要像素精确的纹理映射时,仅缩放x ntexels 大纹理以使其映射到屏幕上的mxn像素大矩形是不够的。您还必须确保映射在子像素级别是正确的 - 即纹素中心映射到相应的像素中心。在您的情况下,您将整个[0,1]范围用作 tex 坐标(这很好),因此必须映射几何体,以便顶点最终位于像素上(以便将相应的 texel 角直接以 1:1 映射到它们上)。

在此示例中,您尚未确切指定哪些类型width和哪些类型height,但该构造(float)width / windowWidth让我假设这些是整数类型。所以让我们做一个简单的例子:

让和。x=0_ 会发生什么:width=3windowWidth=10

xPos = (0.0f + 3 / 2) / 10 * 2 - 1 = 1.0f / 10 * 2 - 1 = - 0.8f

x 维度的比例因子为0.3,模型矩阵的第一行最终为0.3 0 0 -0.24。当乘以你的左右点时,你会得到x_left = -0.54x_right = 0.06。NDC 中的像素边界将位于{-1, -0.8, -0.6, -0.4, -0.2, 0.0, 0.2, 0.4, 0.6, 0.8, 10}10 像素宽的视口处,因此您不是以像素精确的方式进行绘制。

您的翻译对于像素精确的绘图根本没有意义,尤其是首先翻译然后缩放结果空间的奇怪方式。


推荐阅读