如何在 webgl 着色器中偏移 2D 三角形?

how to offset a 2D triangle in a webgl shader?

我想我遇到了一个愚蠢的案例,我计划实施一些保守的深度渲染 (conservative rendering in CPU gems),但我停留在最基本的层面:扩大三角形。

情况如下: http://nraynaud.github.io/webgcode/test_3D_conservative_rendering.html (黑色三角为输入,红点为放大顶点,方块代表采样网格)

我将 webgl 与 thee.js 一起使用,我将每个顶点的 2 个伴随顶点作为属性发送,并且我试图在顶点着色器中放大三角形。我可能处于最愚蠢的情况:我有一个正交相机,其 'up' 是 +Y 轴,视图延伸到 -Z(基本上我正在拍摄我的脚,屏幕顶部是 +Y ).我不能(我认为,射影几何有点神秘)直接使用来自 CPU gems 的代码,因为它假设有一个透视相机(它计算穿过三角形边缘的平面和观看者,而正射中没有视点)。

这是我尝试过的:

        '    vec3 p1 = prevPoint, p2 = position, p3 = nextPoint;',
        '    if (!isFrontFacing(p1.xy, p2.xy, p3.xy)) {vec3 temp = p1;p1 = p3; p3 = temp;}',
        //move the vertex in 2D
        '    vec2 e1 = p2.xy - p1.xy;',
        '    vec2 e1Normal = normalize(vec2(e1.y, -e1.x));',
        '    vec2 e1Translate = e1Normal * length(hPixelWorld);',

        '    vec2 e2 = p3.xy - p2.xy;',
        '    vec2 e2Normal = normalize(vec2(e2.y, -e2.x));',
        '    vec2 e2Translate = e2Normal * length(hPixelWorld);',

我认为这应该给我与 p2(考虑的顶点)相邻的 2 条边的 2 条法线,都指向三角形的内部或外部,这取决于我的运气。好吧,实际上,根据所考虑的顶点,我并不总是让法线指向同一方向(并且没有缺点),而且我没有任何解释(我什至尝试使用 isFrontFacing 来保护我的代码以防止顶点顺序() 在顶部)。

我尝试了一些愚蠢的东西,比如计算法线方向的所有 4 种可能性并选择使我距离三角形质心最远的点,但我认为我什至搞砸了它的实现,因为一些点显然不是离中心最远的:

        '    vec3 possibilities[4];',
        '    vec2 centroid = ((p1+p2+p3)/3.0).xy;',
        '    possibilities[0] = translatePoint(p2.xy, +e1Translate, +e2Translate, e1, e2, centroid);',
        '    possibilities[1] = translatePoint(p2.xy, -e1Translate, +e2Translate, e1, e2, centroid);',
        '    possibilities[2] = translatePoint(p2.xy, -e1Translate, -e2Translate, e1, e2, centroid);',
        '    possibilities[3] = translatePoint(p2.xy, +e1Translate, -e2Translate, e1, e2, centroid);',
        '    vec3 currentBest = possibilities[0];',
        '    if (possibilities[1].z >= currentBest.z)',
        '        currentBest = possibilities[1];',
        '    if (possibilities[2].z >= currentBest.z)',
        '        currentBest = possibilities[2];',
        '    if (possibilities[3].z >= currentBest.z)',
        '        currentBest = possibilities[3];',

        '    vec2 resultPoint2D = currentBest.xy;',

这里是 isFrontFacing:

        'bool isFrontFacing(vec2 p1, vec2 p2, vec2 p3) {',
        '    float a = cross2d(p2-p1, p2-p3);',
        '    return a > 0.0;',
        '}',

这里是翻译点:

        // we are encoding the distance to centroid in z
        'vec3 translatePoint(vec2 point, vec2 dir1, vec2 dir2, vec2 e1, vec2 e2, vec2 centroid) {',
        '    vec2 e1TranslatedPoint = point + dir1;',
        '    vec2 e2TranslatedPoint = point + dir2;',
        //
        '    float u = cross2d(e2TranslatedPoint - e1TranslatedPoint, e2) / cross2d(e2, e1);',
        '    vec2 result = e1TranslatedPoint + u * e1;',
        '    float dist = length(result - centroid);',
        '    return vec3(result, dist);',
        '}',

和cross2D:

        'float cross2d(vec2 v1, vec2 v2) {',
        '    return v1.x * v2.y - v1.y * v2.x; ',
        '}',

完整代码如下: https://github.com/nraynaud/webgcode/blob/01550668fdedba5ab61e2fbf5ed9917ca06b5e75/test_3D_conservative_rendering.html

我们看到只有一个顶点在正确的方向上移动,对于其他 2 个顶点,其中一条边的法线是错误的。

好的,那么当我们不是几何白痴时,我们如何偏移三角形?

我认为您不需要实际遍历法线方向候选的所有组合——因为偏移沿着当前顶点的角平分线,您已经知道实际方向已经从总和来自其他两个三角形顶点的方向向量 p1p2

如果你画个图,你会发现偏移原点的距离与两个方向向量夹角的一半的正弦值成正比。

float pixelSize = 10.0; // or whatever

void main() { 
    vec3 a = normalize(position - prev);
    vec3 b = normalize(position - next);

    // we will offset along the vector `c`
    vec3 c = normalize(a + b);

    // a formula to calculate the sine of half an angle
    float halfsine = sqrt((1.0 - dot(a, b)) / 2.0);

    gl_Position = vec4(position + pixelSize * c / halfsine, 1);
}

这是一张餐巾纸图:向量 AB 是其他顶点的归一化方向。 pixel 显示偏移大小。小直角三角形是全等的,因为新旧顶点位置与两条边线的距离相等。