You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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转换,渐变会变得正确,但其他颜色会过暗:
渐变正确但透明度错误的效果
注意此时最右侧的滑块(黑到白)变成了感知线性的,但左侧主拾取器的黑色到透明的渐变仍然不对。

另外,我对比了用顶点属性生成的黑红渐变和纹理生成的黑红渐变(上方是顶点属性生成的,下方是纹理生成的),两者存在明显差异:
顶点属性渐变 vs 纹理渐变

复现步骤

  • git clone --recursive https://github.com/SaschaWillems/Vulkan
  • Windows环境下运行 cmake -G "Visual Studio 16 2019" -A x64
  • 修改 base/VulkanSwapChain.cpp:188,切换 VK_FORMAT_B8G8R8A8_UNORMVK_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 };

我的测试结果如下:
sRGB vs UNORM四边形效果对比
左上角的红色渐变看起来正确,但右上角的过暗;左下角的红色渐变过亮,但右下角的看起来正确。

备注:内容来源于stack exchange,提问作者Alex Cazacu

火山引擎 最新活动