简体   繁体   中英

Cube using single GL_TRIANGLE_STRIP

Is it possible to draw a whole cube using just a single GL_TRIANGLE_STRIP ?

Obviously it's just the cube combinatorics I'm concerned about here, it might as well be stretched into any kind of box or similar object.

From the paper Optimizing Triangle Strips for Fast Rendering by Evans, Skiena, and Varshney:

三角带图

For those of you who are lazy (like me), here's a copy-paste version of rob mayoff's answer ;)

static const GLfloat cube_strip[] = {
    -1.f, 1.f, 1.f,     // Front-top-left
    1.f, 1.f, 1.f,      // Front-top-right
    -1.f, -1.f, 1.f,    // Front-bottom-left
    1.f, -1.f, 1.f,     // Front-bottom-right
    1.f, -1.f, -1.f,    // Back-bottom-right
    1.f, 1.f, 1.f,      // Front-top-right
    1.f, 1.f, -1.f,     // Back-top-right
    -1.f, 1.f, 1.f,     // Front-top-left
    -1.f, 1.f, -1.f,    // Back-top-left
    -1.f, -1.f, 1.f,    // Front-bottom-left
    -1.f, -1.f, -1.f,   // Back-bottom-left
    1.f, -1.f, -1.f,    // Back-bottom-right
    -1.f, 1.f, -1.f,    // Back-top-left
    1.f, 1.f, -1.f      // Back-top-right
};

Yes, after a bit of experimenting I found the answer myself. Imagine the corners of your cube are colored alternatingly black and white. Draw a triangle edge along each face between the two black corners. That way, the diagonals form a tetrahedron inside the cube. For the [0,1]³ cube, a possible sequence of coordinates would be the following:

Vertex  Triangle    Face
------+-----------+-----
0 0 0
0 1 0
1 0 0  000 010 100  **0
1 1 0  100 010 110  **0
1 1 1  100 110 111  1**
0 1 0  111 110 010  *1*
0 1 1  111 010 011  *1*
0 0 1  011 010 001  0**
1 1 1  011 001 111  **1
1 0 1  111 001 101  **1
1 0 0  111 101 100  1**
0 0 1  100 101 001  *0*
0 0 0  100 001 000  *0*
0 1 0  000 001 010  0**

May be useful to some, here's a geometry shader that will take in a point and output a triangle strip of a unit cube

#version 410

layout(points) in;
layout(triangle_strip, max_vertices = 12) out;

uniform mat4 mvp;

void main() {
    vec4 center = gl_in[0].gl_Position;

    vec4 dx = mvp[0];
    vec4 dy = mvp[1];
    vec4 dz = mvp[2];

    vec4 p1 = center;
    vec4 p2 = center + dx;
    vec4 p3 = center + dy;
    vec4 p4 = p2 + dy;
    vec4 p5 = p1 + dz;
    vec4 p6 = p2 + dz;
    vec4 p7 = p3 + dz;
    vec4 p8 = p4 + dz;

    gl_Position = p7;
    EmitVertex();

    gl_Position = p8;
    EmitVertex();

    gl_Position = p5;
    EmitVertex();

    gl_Position = p6;
    EmitVertex();

    gl_Position = p2;
    EmitVertex();

    gl_Position = p8;
    EmitVertex();

    gl_Position = p4;
    EmitVertex();

    gl_Position = p7;
    EmitVertex();

    gl_Position = p3;
    EmitVertex();

    gl_Position = p5;
    EmitVertex();

    gl_Position = p1;
    EmitVertex();

    gl_Position = p2;
    EmitVertex();

    gl_Position = p3;
    EmitVertex();

    gl_Position = p4;
    EmitVertex();

}

This version also has texture coordinates and normals just in case you need those.

I tested this with WebGL and the cube is texture mapped correctly but has peculiarities due to the fact that you really need three texture coordinates for each corner - one for each face. Because of this you can use these UVs to texture map 4 sides of the cube, but the top and bottom will not have proper textures.

const Cube = function () {
    const u0 = 0;
    const u1 = 1;

    const v0 = 0;
    const v1 = 1 / 3;
    const v2 = 2 / 3;
    const v3 = 1;

    const verticies = [
        +1, +1, -1,
        -1, +1, -1,
        +1, -1, -1,
        -1, -1, -1,
        +1, +1, +1,
        -1, +1, +1,
        -1, -1, +1,
        +1, -1, +1,
    ];

    const uvs = [
        u0, v3,
        u0, v0,
        u0, v2,
        u0, v1,
        u1, v3,
        u1, v0,
        u1, v1,
        u1, v2,
    ];

    const indexes = [3, 2, 6, 7, 4, 2, 0, 3, 1, 6, 5, 4, 1, 0];

    const v = [];
    const u = [];
    for (var i = 0; i < indexes.length; i++) {
        const corner = indexes[i];
        v.push(verticies[corner * 3 + 0]);
        v.push(verticies[corner * 3 + 1]);
        v.push(verticies[corner * 3 + 2]);

        u.push(uvs[corner * 2 + 0]);
        u.push(uvs[corner * 2 + 1]);
    }

    return {
        verticies: v, 
        uvs: u,
        normals: v
    };
};

The paper mentioned above has the triangles wound in the wrong direction however is there is a simple correction, reverse the order of vertices, and it will become front-facing-outward (not inward).

1 2 5 6 7 2 4 1 3 5 8 7 3 4

And here is a set of normalized vertices for a unit-cube. You can apply a transformation to this and render your bounding-boxes (axis-aligned or oriented).

    static const std::array<float> cube_strip[] = {
        +0.5, +0.5, -0.5, // Back-top-right
        -0.5, +0.5, -0.5, // Back-top-left
        +0.5, -0.5, -0.5, // Back-bottom-right
        -0.5, -0.5, -0.5, // Back-bottom-left
        -0.5, -0.5, +0.5, // Front-bottom-left
        -0.5, +0.5, -0.5, // Back-top-left
        -0.5, +0.5, +0.5, // Front-top-left
        +0.5, +0.5, -0.5, // Back-top-right
        +0.5, +0.5, +0.5, // Front-top-right
        +0.5, -0.5, -0.5, // Back-bottom-right
        +0.5, -0.5, +0.5, // Front-bottom-right
        -0.5, -0.5, +0.5, // Front-bottom-left
        +0.5, +0.5, +0.5, // Front-top-right
        -0.5, +0.5, +0.5, // Front-top-left
    };

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