为什么通过计算到边缘的最近距离来渲染内部阴影时边缘之间有一个亮区?

Why is there a light area between edges when rendering an inner shadow by calculating the nearest distance to an edge?

我正在通过计算每个像素到边缘的最近距离来渲染内部阴影(与内部发光的想法相同,但不是光而是暗的)。这是在片段着色器中完成的,方法是从顶点着色器(使用 varying)插入顶点位置,通过统一数组传递形状的边缘点,并通过 运行 点到计算最近距离-针对每条边的线段距离计算。

这是片段着色器:

precision mediump float;
uniform mediump sampler2D uBaseSampler;
uniform mediump vec2 uVerts[11];
varying mediump vec2 vTexCoord;
varying mediump vec4 vPosition;
float distanceSq(mediump vec2 a, mediump vec2 b)
{
    mediump vec2 c = b - a;
    return dot(c, c);
}
float distSqFromLineSegment(mediump vec2 p, mediump vec2 a, mediump vec2 b)
{
    float lenSq = distanceSq(a, b);
    /*if (lenSq == 0.0) return distanceSq(p, a);*/
    float t = dot(p - a, b - a) / lenSq;
    if (t < 0.0) return distanceSq(p, a);
    else if (t > 1.0) return distanceSq(p, b);
    mediump vec2 proj = a + t * (b - a);
    return distanceSq(p, proj);
}
void main()
{
    float nearestDistSq = 1e3;
    for (lowp int i = 0; i < 10; ++i)
    {
        lowp int j = i + 1;
        float distSq = distSqFromLineSegment(vPosition.xy, uVerts[i], uVerts[j]);
        nearestDistSq = min(nearestDistSq, sqrt(distSq));
    }
    float nearestDist = nearestDistSq;
    float coef = nearestDist;
    lowp vec4 frag = texture2D(uBaseSampler, vTexCoord);
    gl_FragColor = vec4(frag.xyz * coef, 1.0);
}

下面是仅根据边缘 1 计算内部阴影时的样子:

下面是仅根据边 2 计算内部阴影时的样子:

请注意内部阴影对于两条边的正确性。

现在,这是根据两条边计算内部阴影时的样子:

注意到边缘之间的内部亮度了吗?并且,作为参考,这里是基于所有边缘计算内部阴影时的类似结果:

当单独观察由边缘 1 和边缘 2 生成的内部阴影时,看起来不会通过同时考虑两个边缘来形成内部亮度。是什么原因造成的?沿着亮度的点不应比它们的相邻点有更大的距离,因为我正在计算与所有边缘的最小距离。

有什么想法吗?

更新:

根据一些早期的答案,我的问题似乎是我已经实现了 "darken" 算法,并且以这种方式组合两个渐变会导致我的屏幕截图出现副作用。例如,如果你拍摄我的前两张截图,在图像编辑器中将它们叠加,并将第二层设置为 "darken" 混合模式,你将得到我的第三张截图。

我真正想要的是某种乘法。如果我在图像编辑器中使用 "multiply" 混合模式,我会得到想要的外观:

我将如何实现这一目标?看起来我必须为每条边单独计算内部阴影,然后将所有系数相乘,而不是根据最近的边计算一个系数。

更新二:

实施乘法混合模式会导致形状顶点周围的区域变暗。结果比我原来的版本更糟。我决定坚持使用原始版本,因为它快速、正确,并且通过将系数缩放到 0.8-1.0 左右而不是 0.0-1.0 来最小化不需要的副作用。我也接受了让我走上这条路的答案。

图片没有任何问题...OpenGL 正在绘制您所要求的内容,这是与到边缘的距离相关的亮度。距离不会是一个平滑的函数,因此您的眼睛会看到距离函数的导数突然发生变化的边缘。

你可以自己测试一下。打开图像编辑程序,例如 Gimp 或 Photoshop 或其他软件,然后制作两个线性渐变。第二个渐变,选择"darken"作为渐变的合成模式。我得到了下图:

你可以看到这里有一条类似的45度线

如果你想去除锐利的边缘,你必须做更多的事情,比如模糊结果。距离场通常会有锐利的边缘。