c++ - 宽度不均匀时纹理缩放错误
问题描述
我试图让多个纹理在 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);
}
解决方案
m
当您想要像素精确的纹理映射时,仅缩放x n
texels 大纹理以使其映射到屏幕上的m
xn
像素大矩形是不够的。您还必须确保映射在子像素级别是正确的 - 即纹素中心映射到相应的像素中心。在您的情况下,您将整个[0,1]
范围用作 tex 坐标(这很好),因此必须映射几何体,以便顶点最终位于像素角上(以便将相应的 texel 角直接以 1:1 映射到它们上)。
在此示例中,您尚未确切指定哪些类型width
和哪些类型height
,但该构造(float)width / windowWidth
让我假设这些是整数类型。所以让我们做一个简单的例子:
让和。x=0
_ 会发生什么:width=3
windowWidth=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.54
和x_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 像素宽的视口处,因此您不是以像素精确的方式进行绘制。
您的翻译对于像素精确的绘图根本没有意义,尤其是首先翻译然后缩放结果空间的奇怪方式。
推荐阅读
- android - 您的应用未使用 AndroidX - Flutter 不允许我生成 apk
- xaml - 具有 Materialdesigninxaml 样式的按钮的中心边距定义
- python - 计算数据框中的日期列
- io - Ada.Text_IO.End_Of_File 缓冲副作用
- c# - 抄送电子邮件没有收到c#
- c++ - 为什么我的多线程并行求和函数的向量受限于线程数?
- python - 在 postgres 中将列数据类型从 VARCHAR 转换为 ARRAY(使用 Psycopg2)
- reference - 如何在 LibreOffice 中重新创建页面引用
- ibm-mq - 浏览 IBM MQ 获取消息
- c# - Azure Functions:如何在运行时读取 host.json 中的设置?