opengl - 插值不匹配
问题描述
看这两张图:
两张图片上的红色和蓝色通道都为零,而绿色通道是该点的 x 坐标。x 坐标在 (-1, 1) 范围内。
第一张图像是在 CPU 上计算的:
double screenHeight = 750;
double screenWidth = 750;
double viewportWidth = 2.0;
for (int i = 0; i < screenHeight; ++i) {
for (int j = 0; j < screenWidth; ++j) {
float greenChannel = (double)j / screenWidth * viewportWidth;
}
}
第二个是在 GPU 上计算的:首先,顶点着色器被告知要绘制三个顶点(但没有给出,更多信息在这里):
layout (location = 0) out vec2 outUV;
void main() {
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(vec2(outUV) * 2.0f + -1.0f, 0.0f, 1.0f);
}
这基本上将重心坐标发送到每个片段。它们被插在三个值之间。然后片段着色器进行了以下计算:
layout (location = 0) in vec2 outUV;
layout(location = 0) out vec4 outColor;
void main() {
double viewportWidth = 2.0;
double c = (outUv.x - 0.5) * viewportWidth;
outColor = vec4(vec3(0.0, float(c), 0.0), 1.0);
}
这首先移动 x 坐标,使其现在位于 (-0.5, -0.5) 范围内,然后将其缩放到 (-1, 1) 范围内。
如果您再次查看这些图像,您会发现它们并不相同。CPU 在中心附近更加平滑,而在 GPU 上渲染的过渡更加突然。你知道是什么原因造成的吗?看起来这可能是由于 GPU 某处的精度损失所致。我认为插值的浮点数不够精确,所以我为 c 尝试了不同的方法:
double screenWidth = 750;
double viewportWidth = 2.0;
double c = (gl_FragCoord.xy - 0.5 * screenWidth )/ screenWidth * viewportWidth;
这使用 gl_FragCoord 和屏幕的分辨率,而不是依赖于插值。它给了我与第一次尝试无法区分的结果,所以我猜这不是插值。可以肯定的是,我用两种方法计算了 c 并进行了比较。直到小数点后 7 位,它们都完全相同。
有谁知道为什么这两个图像不一样?
解决方案
GPU 图像是正确的——似乎您启用了 sRGB 处理(这很好!)。因此,您的 GPU 代码以线性方式进行插值,然后应用 sRGB 伽玛压缩函数。在 OpenGL 中,此行为由glEnable/Disable(GL_FRAMEBUFFER_SRGB)
. 在 Vulkan 中,它是在创建交换链时配置的。
您的 CPU 代码在“错误的”色彩空间中进行插值——即直接在 sRGB 值中进行计算,就好像它们是线性的一样,即使它们不是线性的。
您可以按如下方式修复 CPU 代码:
float greenChannel = ...;
greenChannel = greenChannel <= 0.0031308f ? 12.92f*greenChannel : 1.055f*powf(greenChannel, 1/2.4f) - 0.055f;
推荐阅读
- c# - EF Core 3.0 SumAsync 触发聚合函数异常
- c++ - Visual Studio 2017 (C++):在同一解决方案中从项目 B 中的项目 A 访问变量?
- javascript - 检查 CSS 属性的值
- c# - 在 MongoDB C# 驱动程序中插入包含对另一个实体的引用的实体
- java - 面板和按钮已添加到框架中但未出现在框架中
- python - 在python中将命令的输出转换为字典格式
- java - Intellij无法打开maven项目
- python - Python 请求 - 我开始编程
- mysql - 使用 PDO 连接到外部主机时出现奇怪的问题
- uwp - mediainfo.dll 与 UWP 兼容吗?