C++/OpenGL 纹理出现像素化
C++/OpenGL Texture appearing Pixelated
这是我生成纹理的代码(MRE):
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
if(readAlpha)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
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);
glGenerateMipmap(GL_TEXTURE_2D);
以下是 Tex 坐标的生成方式:
for (int y = 0; y < resolution; y++)
{
for (int x = 0; x < resolution; x++)
{
int i = x + y * resolution;
glm::vec2 percent = glm::vec2(x, y) / ((float)resolution - 1);
glm::vec3 pointOnPlane = (percent.x - .5f) * 2 * right + (percent.y - .5f) * 2 * front;
pointOnPlane *= scale;
vertices[i] = Vert();
vertices[i].position = glm::vec3(0.0f);
vertices[i].position.x = (float)pointOnPlane.x;
vertices[i].position.y = (float)pointOnPlane.y;
vertices[i].position.z = (float)pointOnPlane.z;
vertices[i].texCoord = glm::vec2(percent.x, percent.y)*textureScale;
vertices[i].normal = glm::vec3(0.0f);
if (x != resolution - 1 && y != resolution - 1)
{
inds[triIndex] = i;
inds[triIndex + 1] = i + resolution + 1;
inds[triIndex + 2] = i + resolution;
inds[triIndex + 3] = i;
inds[triIndex + 4] = i + 1;
inds[triIndex + 5] = i + resolution + 1;
triIndex += 6;
}
}
}
这是着色器:
垂直:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNorm;
layout (location = 2) in vec2 aTexCoord;
uniform mat4 _PV;
uniform mat4 _Model;
out DATA
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoord;
mat4 PV;
} data_out;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
data_out.FragPos = aPos;
data_out.Normal = aNorm;
data_out.TexCoord = aTexCoord;
data_out.PV = _PV;
}
地理:
#version 330 core
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;
in DATA
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoord;
mat4 PV;
} data_in[];
void main()
{
gl_Position = data_in[0].PV * gl_in[0].gl_Position;
Normal = data_in[0].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[0].FragPos;
EmitVertex();
gl_Position = data_in[0].PV * gl_in[1].gl_Position;
Normal = data_in[1].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[1].FragPos;
EmitVertex();
gl_Position = data_in[0].PV * gl_in[2].gl_Position;
Normal = data_in[2].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[2].FragPos;
EmitVertex();
EndPrimitive();
}
碎片:
#version 330 core
out vec4 FragColor;
uniform vec3 _LightPosition;
uniform vec3 _LightColor;
uniform sampler2D _Diffuse;
//unifrom float _UseTexutres;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
void main()
{
//vec3 objectColor = vec3(0.34f, 0.49f, 0.27f);
vec3 objectColor = vec3(1, 1, 1);
objectColor = texture(_Diffuse, TexCoord).xyz;
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(_LightPosition - FragPos);
float diff = max(dot(norm, lightDir), 0.0f);
vec3 diffuse = diff * _LightColor;
vec3 result = (vec3(0.2, 0.2, 0.2) + diffuse) * objectColor;
FragColor = vec4(result, 1.0);
}
即使我使用的是 8K 纹理,我也会出现像素化纹理。
如果您想查看整个源代码:https://github.com/Jaysmito101/TerraGen3D
结果如下:
你的几何着色器没有意义:
首先,您对输出三角形的所有 3 个顶点使用相同的 data_in.TexCoords[0]
,这意味着为该三角形生成的所有片段将对纹理的完全相同位置进行采样,从而导致完全相同的输出颜色,因此出现了图像的“像素化”结构。就像您已经为 Normal
和 FragPos
所做的那样,您应该转发每个顶点的数据。这应该已经解决了您的问题。
但是,您的方法存在更多问题。您将 mat4 PV
作为每个顶点的数据从 VS 转发到 GS。但是,你转发的数据是一个uniform
,所以这是一种资源浪费。每个着色器阶段都可以访问所有制服,因此无需按顶点转发此数据。
但房间里真正的大象是这个几何着色器应该做的事情。统一矩阵的实际变换可以——而且绝对应该——直接在顶点着色器中执行。而你的几何着色器的其余部分基本上是对传递实现的尝试(只是一个错误的实现)。那么你需要这个着色器做什么?你
可以在VS中做变换,完全去掉geometry shader。在性能方面,这也将是一个胜利,因为几何着色器相当低效,如果不是绝对需要,应该避免。
这是我生成纹理的代码(MRE):
glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id);
if(readAlpha)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
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);
glGenerateMipmap(GL_TEXTURE_2D);
以下是 Tex 坐标的生成方式:
for (int y = 0; y < resolution; y++)
{
for (int x = 0; x < resolution; x++)
{
int i = x + y * resolution;
glm::vec2 percent = glm::vec2(x, y) / ((float)resolution - 1);
glm::vec3 pointOnPlane = (percent.x - .5f) * 2 * right + (percent.y - .5f) * 2 * front;
pointOnPlane *= scale;
vertices[i] = Vert();
vertices[i].position = glm::vec3(0.0f);
vertices[i].position.x = (float)pointOnPlane.x;
vertices[i].position.y = (float)pointOnPlane.y;
vertices[i].position.z = (float)pointOnPlane.z;
vertices[i].texCoord = glm::vec2(percent.x, percent.y)*textureScale;
vertices[i].normal = glm::vec3(0.0f);
if (x != resolution - 1 && y != resolution - 1)
{
inds[triIndex] = i;
inds[triIndex + 1] = i + resolution + 1;
inds[triIndex + 2] = i + resolution;
inds[triIndex + 3] = i;
inds[triIndex + 4] = i + 1;
inds[triIndex + 5] = i + resolution + 1;
triIndex += 6;
}
}
}
这是着色器: 垂直:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNorm;
layout (location = 2) in vec2 aTexCoord;
uniform mat4 _PV;
uniform mat4 _Model;
out DATA
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoord;
mat4 PV;
} data_out;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
data_out.FragPos = aPos;
data_out.Normal = aNorm;
data_out.TexCoord = aTexCoord;
data_out.PV = _PV;
}
地理:
#version 330 core
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;
in DATA
{
vec3 FragPos;
vec3 Normal;
vec2 TexCoord;
mat4 PV;
} data_in[];
void main()
{
gl_Position = data_in[0].PV * gl_in[0].gl_Position;
Normal = data_in[0].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[0].FragPos;
EmitVertex();
gl_Position = data_in[0].PV * gl_in[1].gl_Position;
Normal = data_in[1].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[1].FragPos;
EmitVertex();
gl_Position = data_in[0].PV * gl_in[2].gl_Position;
Normal = data_in[2].Normal;
TexCoord = data_in[0].TexCoord;
FragPos = data_in[2].FragPos;
EmitVertex();
EndPrimitive();
}
碎片:
#version 330 core
out vec4 FragColor;
uniform vec3 _LightPosition;
uniform vec3 _LightColor;
uniform sampler2D _Diffuse;
//unifrom float _UseTexutres;
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;
void main()
{
//vec3 objectColor = vec3(0.34f, 0.49f, 0.27f);
vec3 objectColor = vec3(1, 1, 1);
objectColor = texture(_Diffuse, TexCoord).xyz;
vec3 norm = normalize(Normal);
vec3 lightDir = normalize(_LightPosition - FragPos);
float diff = max(dot(norm, lightDir), 0.0f);
vec3 diffuse = diff * _LightColor;
vec3 result = (vec3(0.2, 0.2, 0.2) + diffuse) * objectColor;
FragColor = vec4(result, 1.0);
}
即使我使用的是 8K 纹理,我也会出现像素化纹理。
如果您想查看整个源代码:https://github.com/Jaysmito101/TerraGen3D
结果如下:
你的几何着色器没有意义:
首先,您对输出三角形的所有 3 个顶点使用相同的 data_in.TexCoords[0]
,这意味着为该三角形生成的所有片段将对纹理的完全相同位置进行采样,从而导致完全相同的输出颜色,因此出现了图像的“像素化”结构。就像您已经为 Normal
和 FragPos
所做的那样,您应该转发每个顶点的数据。这应该已经解决了您的问题。
但是,您的方法存在更多问题。您将 mat4 PV
作为每个顶点的数据从 VS 转发到 GS。但是,你转发的数据是一个uniform
,所以这是一种资源浪费。每个着色器阶段都可以访问所有制服,因此无需按顶点转发此数据。
但房间里真正的大象是这个几何着色器应该做的事情。统一矩阵的实际变换可以——而且绝对应该——直接在顶点着色器中执行。而你的几何着色器的其余部分基本上是对传递实现的尝试(只是一个错误的实现)。那么你需要这个着色器做什么?你 可以在VS中做变换,完全去掉geometry shader。在性能方面,这也将是一个胜利,因为几何着色器相当低效,如果不是绝对需要,应该避免。