简体   繁体   中英

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

I think I have a case of the stupid, I'm planing on implementing some conservative depth rendering ( conservative rendering in CPU gems ) but I'm stuck at the most basic level: enlarging the triangle.

here is the state of affairs: http://nraynaud.github.io/webgcode/test_3D_conservative_rendering.html (the black triangle is the input, the red dots are the enlarged vertices, and the squares represent the sampling grid)

I'm using webgl with thee.js, I sent as attributes the 2 companion vertices of each vertex, and I'm trying to enlarge the triangles in the vertex shader. I am in the most stupid situation possible: I have an orthographic camera whose 'up' is the +Y axis, and the view extends towards -Z (basically I'm filming my feet and the top of the screen is +Y). I can't (I think, projective geometry is a bit mysterious) directly use the code from CPU gems, because it supposes a perspective camera (it computes the planes passing through the edges of the triangle and the eye point of the viewer, while there is no eye point in ortho).

here is what I tried:

        '    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);',

I think this should give me 2 normals to the 2 edges adjacent to p2 (the considered vertex), both pointing inwards or outwards of the triangle, depending on my luck. Well, actually, depending on the considered vertex, I don't always get the normals pointing in the same direction (and not with a cons), and I have no explanation (I even tried to armor my code against vertex order with the isFrontFacing() at the top).

I tried some silly stuff, like computing all the 4 possibilities of normal directions and choosing the one that get me the furthest point from the centroid of the triangle, but I think I even botched the implementation of that, because some points are clearly not the furthest away from the center:

        '    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;',

here is isFrontFacing:

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

here is translatePoint:

        // 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;',
        //http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
        '    float u = cross2d(e2TranslatedPoint - e1TranslatedPoint, e2) / cross2d(e2, e1);',
        '    vec2 result = e1TranslatedPoint + u * e1;',
        '    float dist = length(result - centroid);',
        '    return vec3(result, dist);',
        '}',

and cross2D:

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

here is the complete code: https://github.com/nraynaud/webgcode/blob/01550668fdedba5ab61e2fbf5ed9917ca06b5e75/test_3D_conservative_rendering.html

We see that only one vertex is moved in the correct direction, for the 2 others, one of the edges normals was wrong.

Ok, so how do we offset a triangle when we are not a geometric moron?

I don't think you need to actually iterate through all the combinations of candidates for normal direction -- Since the offset is along the angle bisector of the current vertex, you know the actual direction already just from the sum of the direction vectors p1 and p2 from the other two triangle vertices.

If you draw out a diagram, you will realize the distance to offset from the original position is proportional to the sine of half the angle between the two direction vectors.

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);
}

Here's a napkin diagram: vectors A and B are the normalized directions from the other vertices. pixel shows the offset size. The small right triangles are congruent because both the old and the new vertex positions are equidistant from the two edge lines.

图表

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM