什么是使用 OpenGL 的纹理缩减采样(downscaling)?
What is texture downsampling (downscaling) using OpenGL?
我没有找到任何说明如何使用 OpenGL 缩小纹理的教程。
例如,如果我有一个 1024x720 的纹理,并且我想按因子 1/4 生成缩小比例,该怎么做?
这里a tutorial谈论下采样。
更新
我在片段着色器中尝试了以下过滤代码:
vec4 DownSampleFrame(sampler2D uniformSampler)
{
vec2 pixelOffset = vec2(1.0f/1024.0f, 1.0f/720.0f);
vec3 downScaleColor = vec3(0.0f);
{
downScaleColor += texture(uniformSampler, vec2(TexCoords.x - 4.0f * pixelOffset.x, TexCoords.y)).xyz;
downScaleColor += texture(uniformSampler, vec2(TexCoords.x + 4.0f * pixelOffset.x, TexCoords.y)).xyz;
downScaleColor += texture(uniformSampler, vec2(TexCoords.x, TexCoords.y - 4.0f * pixelOffset.y)).xyz;
downScaleColor += texture(uniformSampler, vec2(TexCoords.x, TexCoords.y + 4.0f * pixelOffset.y)).xyz;
downScaleColor *= 0.25f;
}
return (vec4(downScaleColor, 1.0f));
}
这种代码是指降采样算法吗?
在不知道你想要对纹理进行降采样的原因的情况下,这里是
我能想到的选项是:
- 如果您需要更小的纹理,就使用更小的纹理;您可以在离线过程中预处理较大的纹理
- 通常硬件会进行纹理过滤,您可以使用 MIP 映射; MIP 映射级别的图像可以来自离线过程
离线过程可以使用链接教程中的方法生成
较低的 MIP 贴图级别,首先与高斯滤波器进行卷积以摆脱
的高频,然后丢弃 3/4 的像素(对于两个方向的 2:1 下采样情况)。如果你知道的话
预先丢弃的像素(例如 2:1 在
两个方向)这些像素的卷积计算被浪费了
可以省略。
你当然可以在片段着色器中做到这一点,但你必须确保
采样器设置为不过滤(最近邻)。
这里有几个方面。如果你已经有了全尺寸的纹理,我真的想不出一个很好的理由来创建一个缩小的纹理。如果你真的想以低于全分辨率的分辨率对其进行采样,你可以使用缩小形式。
纹理贴图
在 OpenGL 中使用较低分辨率版本的纹理的主要方法是 mipmapping。您可以为纹理 texId
生成 mipmaps:
glBindTexture(GL_TEXTURE_2D, texId);
glGenerateMipmap(GL_TEXTURE_2D);
对于要使用的 mipmap,必须相应地设置采样参数:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
如果您现在对纹理进行采样,它将使用缩小版本,具体取决于您正在绘制的几何体的大小。
控制 mipmap 级别
如果您只是执行上述操作,则仍将使用全分辨率,具体取决于输出中纹理的缩放比例。您可以通过限制可以使用的 mipmap 级别来防止这种情况。例如,如果您想从采样中排除最低的 4 个 mipmap 级别:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 4);
由于每个 mipmap 级别都是前一个级别的线性分辨率的一半,因此这将以整个纹理线性分辨率的 1/16 进行采样。
缩减规模
如果出于某种原因,您真的想要缩小纹理,有几个选项。假设您想从 texId
的级别 4 创建一个新纹理,并且您创建了如上所示的 mipmap。你可以用这个贴图的4级创建一个FBO作为附件:
GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texId, 4);
然后将内容复制到新的纹理中:
GLuint smallTexId = 0;
glGenTextures(GL_TEXTURE_2D, &smallTexId);
glBindTexture(GL_TEXTURE_2D, smallTexId);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, smallWidth, smallHeight, 0);
另一种解决方案是使用 glBlitFramebuffer()
:
GLuint fboIds[2] = {0};
glGenFramebuffers(2, fboIds);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboIds[0]);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texId, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboIds[1]);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, smallTexId, 0);
glBlitFramebuffer(0, 0, width, height,
0, 0, smallWidth, smallHeight,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
在这种情况下,您不需要在原始纹理上使用 mipmap。即使下采样的质量可能不是很好,如果它超过 2 倍。
我没有找到任何说明如何使用 OpenGL 缩小纹理的教程。
例如,如果我有一个 1024x720 的纹理,并且我想按因子 1/4 生成缩小比例,该怎么做?
这里a tutorial谈论下采样。
更新
我在片段着色器中尝试了以下过滤代码:
vec4 DownSampleFrame(sampler2D uniformSampler)
{
vec2 pixelOffset = vec2(1.0f/1024.0f, 1.0f/720.0f);
vec3 downScaleColor = vec3(0.0f);
{
downScaleColor += texture(uniformSampler, vec2(TexCoords.x - 4.0f * pixelOffset.x, TexCoords.y)).xyz;
downScaleColor += texture(uniformSampler, vec2(TexCoords.x + 4.0f * pixelOffset.x, TexCoords.y)).xyz;
downScaleColor += texture(uniformSampler, vec2(TexCoords.x, TexCoords.y - 4.0f * pixelOffset.y)).xyz;
downScaleColor += texture(uniformSampler, vec2(TexCoords.x, TexCoords.y + 4.0f * pixelOffset.y)).xyz;
downScaleColor *= 0.25f;
}
return (vec4(downScaleColor, 1.0f));
}
这种代码是指降采样算法吗?
在不知道你想要对纹理进行降采样的原因的情况下,这里是 我能想到的选项是:
- 如果您需要更小的纹理,就使用更小的纹理;您可以在离线过程中预处理较大的纹理
- 通常硬件会进行纹理过滤,您可以使用 MIP 映射; MIP 映射级别的图像可以来自离线过程
离线过程可以使用链接教程中的方法生成 较低的 MIP 贴图级别,首先与高斯滤波器进行卷积以摆脱 的高频,然后丢弃 3/4 的像素(对于两个方向的 2:1 下采样情况)。如果你知道的话 预先丢弃的像素(例如 2:1 在 两个方向)这些像素的卷积计算被浪费了 可以省略。
你当然可以在片段着色器中做到这一点,但你必须确保 采样器设置为不过滤(最近邻)。
这里有几个方面。如果你已经有了全尺寸的纹理,我真的想不出一个很好的理由来创建一个缩小的纹理。如果你真的想以低于全分辨率的分辨率对其进行采样,你可以使用缩小形式。
纹理贴图
在 OpenGL 中使用较低分辨率版本的纹理的主要方法是 mipmapping。您可以为纹理 texId
生成 mipmaps:
glBindTexture(GL_TEXTURE_2D, texId);
glGenerateMipmap(GL_TEXTURE_2D);
对于要使用的 mipmap,必须相应地设置采样参数:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
如果您现在对纹理进行采样,它将使用缩小版本,具体取决于您正在绘制的几何体的大小。
控制 mipmap 级别
如果您只是执行上述操作,则仍将使用全分辨率,具体取决于输出中纹理的缩放比例。您可以通过限制可以使用的 mipmap 级别来防止这种情况。例如,如果您想从采样中排除最低的 4 个 mipmap 级别:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 4);
由于每个 mipmap 级别都是前一个级别的线性分辨率的一半,因此这将以整个纹理线性分辨率的 1/16 进行采样。
缩减规模
如果出于某种原因,您真的想要缩小纹理,有几个选项。假设您想从 texId
的级别 4 创建一个新纹理,并且您创建了如上所示的 mipmap。你可以用这个贴图的4级创建一个FBO作为附件:
GLuint fboId = 0;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboId);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texId, 4);
然后将内容复制到新的纹理中:
GLuint smallTexId = 0;
glGenTextures(GL_TEXTURE_2D, &smallTexId);
glBindTexture(GL_TEXTURE_2D, smallTexId);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, smallWidth, smallHeight, 0);
另一种解决方案是使用 glBlitFramebuffer()
:
GLuint fboIds[2] = {0};
glGenFramebuffers(2, fboIds);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboIds[0]);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, texId, 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboIds[1]);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, smallTexId, 0);
glBlitFramebuffer(0, 0, width, height,
0, 0, smallWidth, smallHeight,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
在这种情况下,您不需要在原始纹理上使用 mipmap。即使下采样的质量可能不是很好,如果它超过 2 倍。