Vulkan中基于顶点属性的感知线性渐变与sRGB表面适配问题咨询
Vulkan中基于顶点属性的感知线性渐变与sRGB表面适配问题咨询
我正在尝试用两个重叠的四边形实现一个简单的颜色拾取器:一个四边形是水平方向从白色到对应色值的渐变,另一个是垂直方向从黑色到透明的渐变。
当前的渲染设置如下:
- 颜色作为顶点属性以线性空间指定
- 表面格式为
VK_FORMAT_B8G8R8A8_SRGB - 表面色彩空间为
VK_COLOR_SPACE_SRGB_NONLINEAR_KHR - 顶点/片段着色器中未执行额外的线性转sRGB或sRGB转线性转换
但最终得到的结果不符合预期:
颜色过亮(黑色和红色占比太少)。
正确的红色系颜色拾取器应该是这样的:
另外,最右侧的滑块也有问题:它从纯白插值到纯黑,渐变是线性的(感知上是非线性的)。
我现在有个核心疑问:既然顶点属性颜色是线性空间指定的(其实单个通道不是0就是1:白色是[1,1,1],红色是[1,0,0],所以通道值本身不影响),片段插值也在线性空间完成,那为什么最终输出是线性的(感知非线性)?毕竟表面是sRGB格式,Vulkan不是应该在片段着色器之后自动完成线性到sRGB的转换吗?
我尝试过在像素着色器中应用线性转sRGB转换,渐变会变得正确,但其他颜色会过暗:
注意此时最右侧的滑块(黑到白)变成了感知线性的,但左侧主拾取器的黑色到透明的渐变仍然不对。
另外,我对比了用顶点属性生成的黑红渐变和纹理生成的黑红渐变(上方是顶点属性生成的,下方是纹理生成的),两者存在明显差异:
复现步骤
git clone --recursive https://github.com/SaschaWillems/Vulkan- Windows环境下运行
cmake -G "Visual Studio 16 2019" -A x64 - 修改
base/VulkanSwapChain.cpp:188,切换VK_FORMAT_B8G8R8A8_UNORM和VK_FORMAT_B8G8R8A8_SRGB - 修改
examples/triangle/triangle.cpp:226中的顶点数据为红到白渐变:
// Setup vertices std::vector<Vertex> vertexBuffer{ { { 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }, // br { { -1.0f, 1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f } }, // bl { { -1.0f, -1.0f, 0.0f }, { 1.0f, 1.0f, 1.0f } }, // tl { { 1.0f, -1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }, // tr };
或者修改为彩色四边形:
// Setup vertices std::vector<Vertex> vertexBuffer{ { { 1.0f, 1.0f, 0.0f }, { 1.0f, 0.0f, 0.0f } }, // br { { -1.0f, 1.0f, 0.0f }, { 0.0f, 1.0f, 0.0f } }, // bl { { -1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }, // tl { { 1.0f, -1.0f, 0.0f }, { 0.0f, 0.0f, 1.0f } }, // tr };
- 修改
examples/triangle/triangle.cpp:233中的索引为:
// Setup indices std::vector<uint32_t> indexBuffer{ 0, 1, 2, 0, 2, 3 };
我的测试结果如下:
左上角的红色渐变看起来正确,但右上角的过暗;左下角的红色渐变过亮,但右下角的看起来正确。
备注:内容来源于stack exchange,提问作者Alex Cazacu




