无法将纹理应用于 Qt OpenGL 中的顶点网格
Can't apply texture to grid of vertices in Qt OpenGL
现在我正在创建一个基于高度图的地形网格,类似于 Lighthouse 3D Terrain Tutorial,只是我使用的是 VBO 和 EBO。在我尝试对网格进行纹理化之前,一切都进行得很顺利。目前我正在应用一种跨越整个网格的纹理。使用 Window 7 的样本水母图片,我得到了这个:
对于熟悉图片的人,您可以看到它在整个地形网格中重复了多次。这让我相信我的 UV 坐标被破坏了。但是,如果我使用一个总是 returns 0
的函数来确定每个网格顶点的高度,我最终会得到:
现在我很困惑,我似乎找不到任何其他资源来帮助我。
我的代码如下:
generate_terrain()
函数:
QImage terrainImage;
terrainImage.load(imagePath.data());
int width = terrainImage.width();
int height = terrainImage.height();
float uStep = 1.0f / width;
float vStep = 1.0f / height;
grid = new std::vector<float>;
indices = new std::vector<unsigned short>;
for (int i = 0; i <= height-1; ++i) {
for (int j = 0; j <= width-1; ++j) {
QVector3D vertex1{j, heightFunction(terrainImage.pixel(j, i)), i};
QVector3D vertex2{j, heightFunction(terrainImage.pixel(j, i+1)), i+1};
QVector3D vertex3{j+1, heightFunction(terrainImage.pixel(j+1, i+1)), i+1};
QVector3D edge1 = vertex2 - vertex1;
QVector3D edge2 = vertex3 - vertex1;
QVector3D normal = QVector3D::crossProduct(edge1, edge2);
normal.normalize();
grid->push_back(vertex1.x());
grid->push_back(vertex1.y());
grid->push_back(vertex1.z());
grid->push_back(normal.x());
grid->push_back(normal.y());
grid->push_back(normal.z());
grid->push_back(j * uStep);
grid->push_back(i * vStep);
}
}
for (int i = 0; i < height-1; ++i) {
for (int j = 0; j < width-1; ++j) {
indices->push_back(i * width + j);
indices->push_back((i+1) * width + j);
indices->push_back((i+1) * width + (j+1));
indices->push_back((i+1) * width + (j+1));
indices->push_back(i * width + (j+1));
indices->push_back(i * width + j);
}
}
vertices = grid->size()/8;
indexCount = indices->size();
纹理加载:
f->glGenTextures(1, &textureId);
f->glBindTexture(GL_TEXTURE_2D, textureId);
QImage texture;
texture.load(texturePath.data());
QImage glTexture = QGLWidget::convertToGLFormat(texture);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexture.width(), glTexture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, glTexture.bits());
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
绘图:
f->glActiveTexture(GL_TEXTURE0);
f->glBindTexture(GL_TEXTURE_2D, textureId);
program->setUniformValue(textureUniform.data(), 0);
f->glBindBuffer(GL_ARRAY_BUFFER, vbo.bufferId());
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 3));
f->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 6));
f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo.bufferId());
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);
f->glDisableVertexAttribArray(2);
f->glDisableVertexAttribArray(1);
f->glDisableVertexAttribArray(0);
着色器:
顶点:
attribute vec3 vertex_modelspace;
attribute vec3 normal_in;
attribute vec2 uv_in;
uniform mat4 mvp;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 lightPosition;
varying vec2 uv;
varying vec3 normal;
varying vec3 fragPos;
void main(void)
{
gl_Position = projection * view * model * vec4(vertex_modelspace, 1);
uv = uv_in;
normal = normal_in;
fragPos = vec3(model * vec4(vertex_modelspace, 1));
}
片段:
varying vec2 uv;
varying vec3 normal;
varying vec3 fragPos;
uniform sampler2D texture;
uniform vec3 lightPosition;
void main(void)
{
vec3 lightColor = vec3(0.6, 0.6, 0.6);
float ambientStrength = 0.2;
vec3 ambient = ambientStrength * lightColor;
vec3 norm = normalize(normal);
vec3 lightDirection = normalize(lightPosition - fragPos);
float diff = max(dot(norm, lightDirection), 0.0);
vec3 diffuse = diff * lightColor;
vec3 color = texture2D(texture, uv).rgb;
vec3 result = (ambient + diffuse) * color;
gl_FragColor = vec4(result, 1.0);
}
我完全卡住了,欢迎提出任何建议:)
P.S。我也在努力让我的灯光看起来更好,所以也欢迎任何相关提示。
您的代码假定属性位置的值,这些值用作 glVertexAttribPointer()
和 glEnableVertexAttribArray()
的第一个参数。例如这里:
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 3));
f->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 6));
您假设这些位置的位置为 0,法线位置为 1,纹理坐标位置为 2。
您当前在代码中的任何内容都不能保证这一点。 GLSL 代码中 attribute
声明的顺序 而不是 定义位置分配。例如来自 OpenGL 3.2 规范:
When a program is linked, any active attributes without a binding specified through BindAttribLocation will be automatically be bound to vertex attributes by the GL.
请注意,这并未指定如何完成位置的自动分配。这意味着它依赖于实现。
要解决此问题,有两种方法:
- 在链接着色器程序之前,您可以为所有属性调用
glBindAttribLocation()
。
- 链接程序后,可调用
glGetAttribLocation()
查询自动分配的位置。
- 在较新的 OpenGL 版本(GLSL 3.30 及更高版本,即与 OpenGL 3.3 匹配的版本)中,您还可以选择直接在 GLSL 代码中指定位置,使用形式为
layout(location=...)
的限定符。
None 这些选项与其他选项相比有任何主要优势。只需根据您的喜好和软件架构使用最适合的那个。
现在我正在创建一个基于高度图的地形网格,类似于 Lighthouse 3D Terrain Tutorial,只是我使用的是 VBO 和 EBO。在我尝试对网格进行纹理化之前,一切都进行得很顺利。目前我正在应用一种跨越整个网格的纹理。使用 Window 7 的样本水母图片,我得到了这个:
对于熟悉图片的人,您可以看到它在整个地形网格中重复了多次。这让我相信我的 UV 坐标被破坏了。但是,如果我使用一个总是 returns 0
的函数来确定每个网格顶点的高度,我最终会得到:
现在我很困惑,我似乎找不到任何其他资源来帮助我。
我的代码如下:
generate_terrain()
函数:
QImage terrainImage;
terrainImage.load(imagePath.data());
int width = terrainImage.width();
int height = terrainImage.height();
float uStep = 1.0f / width;
float vStep = 1.0f / height;
grid = new std::vector<float>;
indices = new std::vector<unsigned short>;
for (int i = 0; i <= height-1; ++i) {
for (int j = 0; j <= width-1; ++j) {
QVector3D vertex1{j, heightFunction(terrainImage.pixel(j, i)), i};
QVector3D vertex2{j, heightFunction(terrainImage.pixel(j, i+1)), i+1};
QVector3D vertex3{j+1, heightFunction(terrainImage.pixel(j+1, i+1)), i+1};
QVector3D edge1 = vertex2 - vertex1;
QVector3D edge2 = vertex3 - vertex1;
QVector3D normal = QVector3D::crossProduct(edge1, edge2);
normal.normalize();
grid->push_back(vertex1.x());
grid->push_back(vertex1.y());
grid->push_back(vertex1.z());
grid->push_back(normal.x());
grid->push_back(normal.y());
grid->push_back(normal.z());
grid->push_back(j * uStep);
grid->push_back(i * vStep);
}
}
for (int i = 0; i < height-1; ++i) {
for (int j = 0; j < width-1; ++j) {
indices->push_back(i * width + j);
indices->push_back((i+1) * width + j);
indices->push_back((i+1) * width + (j+1));
indices->push_back((i+1) * width + (j+1));
indices->push_back(i * width + (j+1));
indices->push_back(i * width + j);
}
}
vertices = grid->size()/8;
indexCount = indices->size();
纹理加载:
f->glGenTextures(1, &textureId);
f->glBindTexture(GL_TEXTURE_2D, textureId);
QImage texture;
texture.load(texturePath.data());
QImage glTexture = QGLWidget::convertToGLFormat(texture);
f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, glTexture.width(), glTexture.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, glTexture.bits());
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
绘图:
f->glActiveTexture(GL_TEXTURE0);
f->glBindTexture(GL_TEXTURE_2D, textureId);
program->setUniformValue(textureUniform.data(), 0);
f->glBindBuffer(GL_ARRAY_BUFFER, vbo.bufferId());
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 3));
f->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 6));
f->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo.bufferId());
f->glEnableVertexAttribArray(0);
f->glEnableVertexAttribArray(1);
f->glEnableVertexAttribArray(2);
f->glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0);
f->glDisableVertexAttribArray(2);
f->glDisableVertexAttribArray(1);
f->glDisableVertexAttribArray(0);
着色器:
顶点:
attribute vec3 vertex_modelspace;
attribute vec3 normal_in;
attribute vec2 uv_in;
uniform mat4 mvp;
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;
uniform vec3 lightPosition;
varying vec2 uv;
varying vec3 normal;
varying vec3 fragPos;
void main(void)
{
gl_Position = projection * view * model * vec4(vertex_modelspace, 1);
uv = uv_in;
normal = normal_in;
fragPos = vec3(model * vec4(vertex_modelspace, 1));
}
片段:
varying vec2 uv;
varying vec3 normal;
varying vec3 fragPos;
uniform sampler2D texture;
uniform vec3 lightPosition;
void main(void)
{
vec3 lightColor = vec3(0.6, 0.6, 0.6);
float ambientStrength = 0.2;
vec3 ambient = ambientStrength * lightColor;
vec3 norm = normalize(normal);
vec3 lightDirection = normalize(lightPosition - fragPos);
float diff = max(dot(norm, lightDirection), 0.0);
vec3 diffuse = diff * lightColor;
vec3 color = texture2D(texture, uv).rgb;
vec3 result = (ambient + diffuse) * color;
gl_FragColor = vec4(result, 1.0);
}
我完全卡住了,欢迎提出任何建议:)
P.S。我也在努力让我的灯光看起来更好,所以也欢迎任何相关提示。
您的代码假定属性位置的值,这些值用作 glVertexAttribPointer()
和 glEnableVertexAttribArray()
的第一个参数。例如这里:
f->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), 0);
f->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 3));
f->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void *) (sizeof(float) * 6));
您假设这些位置的位置为 0,法线位置为 1,纹理坐标位置为 2。
您当前在代码中的任何内容都不能保证这一点。 GLSL 代码中 attribute
声明的顺序 而不是 定义位置分配。例如来自 OpenGL 3.2 规范:
When a program is linked, any active attributes without a binding specified through BindAttribLocation will be automatically be bound to vertex attributes by the GL.
请注意,这并未指定如何完成位置的自动分配。这意味着它依赖于实现。
要解决此问题,有两种方法:
- 在链接着色器程序之前,您可以为所有属性调用
glBindAttribLocation()
。 - 链接程序后,可调用
glGetAttribLocation()
查询自动分配的位置。 - 在较新的 OpenGL 版本(GLSL 3.30 及更高版本,即与 OpenGL 3.3 匹配的版本)中,您还可以选择直接在 GLSL 代码中指定位置,使用形式为
layout(location=...)
的限定符。
None 这些选项与其他选项相比有任何主要优势。只需根据您的喜好和软件架构使用最适合的那个。