在三维模型显示与交互任务中, 坐标轴和外包框是其基本的辅助工具。相关方法可以分成几何体(网格构造)算法与着色器方法(点精灵方式等),本文仅讨论相关的网格生成。

考虑到坐标轴与外包框可以用柱体与锥体两大基本元素组合生成,基本元素又可进一步划分为由采样点构成的圆周,圆面和锥体斜面都可由采样点圆周与中心点构成扇形三角形区域,圆柱面则由相邻两个灯采样点圆周构成三角形条带。 扇形三角形区域和三角形条带均可用基本共享顶点数据结构表示,处理好三角形面朝向问题,则有如下代码实现:

// 坐标轴网格构建
// 1. 坐标轴的尖顶帽子效果可用等圆周三角形条带和扇形三角形实现
// 2. 坐标轴的交界部分可使用扇形三角形实现
YbMesh::indicesTriMesh<glm::vec3> YbMesh::geometry::make_axes(float height,float r) {
    auto v = std::make_shared<std::vector<glm::vec3>>();
    auto f = std::make_shared<std::vector<glm::ivec3>>();

    // z
    for(float delta = 2*PI; delta >= 0; delta -= PI/10)
        v->emplace_back(glm::vec3(2*r*std::sinf(delta), 2*r*std::cosf(delta), height+r*2));

    for(float h = 1.0f; h >= 0; h -= 1.0f/4) {
        for(float delta = 2*PI; delta >= 0; delta -= PI/10)
            v->emplace_back(glm::vec3(r*std::sinf(delta), r*std::cosf(delta), height*h+r*2));
    }
    v->emplace_back(glm::vec3(.0f, .0f, height+r*5));
    v->emplace_back(glm::vec3(.0f, .0f, 0.0f));

    // y
    for(float delta = 2*PI; delta >= 0; delta -= PI/10)
        v->emplace_back(glm::vec3(2*r*std::cosf(delta), height+r*2, 2*r*std::sinf(delta)));

    for(float h = 1.0f; h >= 0; h -= 1.0f/4) {
        for(float delta = 2*PI; delta >= 0; delta -= PI/10)
            v->emplace_back(glm::vec3(r*std::cosf(delta), height*h+r*2, r*std::sinf(delta)));
    }
    v->emplace_back(glm::vec3(.0f, height+r*5, .0f));
    v->emplace_back(glm::vec3(.0f, 0.0f, .0f));

    // x
    for(float delta = 2*PI; delta >= 0; delta -= PI/10)
        v->emplace_back(glm::vec3(height+r*2, 2*r*std::sinf(delta),  2*r*std::cosf(delta)));

    for(float h = 1.0f; h >= 0; h -= 1.0f/4) {
        for(float delta = 2*PI; delta >= 0; delta -= PI/10)
            v->emplace_back(glm::vec3(height*h+r*2, r*std::sinf(delta), r*std::cosf(delta)));
    }
    v->emplace_back(glm::vec3(height+r*5, .0f, .0f));
    v->emplace_back(glm::vec3(.0f, 0.0f, .0f));

    for(int offset:{0,122,244}){
        for(int j = 0; j < 20; j++) {
            // top-down faces
            f->emplace_back(glm::ivec3(j,(j+1)%20,120)+offset);
            f->emplace_back(glm::ivec3(100+(j+1)%20,100+j,121)+offset);
            for(int i = 0; i < 5; i++) {
                f->emplace_back(glm::ivec3(i*20+(j+1)%20,i*20+j,(1+i)*20+j)+offset);
                f->emplace_back(glm::ivec3((i+1)*20+j,(i+1)*20+(j+1)%20,i*20+(j+1)%20)+offset);
            }
        }
    }

    return YbMesh::indicesTriMesh<glm::vec3>(v,f);
}
// 外包框
// 1. 棱的非交界部分用等采样点圆周的三角形条带实现
// 2. 棱的交界部分用扇形三角形实现
// 3. 外包框的8个视觉顶点的坐标位置与索引号的编码关系(难点)
YbMesh::indicesTriMesh<glm::vec3> YbMesh::geometry::make_box(float height,float r) {
    auto v = std::make_shared<std::vector<glm::vec3>>();
    auto f = std::make_shared<std::vector<glm::ivec3>>();

    // z directions
    for(int e:{0,1,2,3}) {
        glm::vec3 offset = 0.5f*height*glm::vec3(e&2?-1.0f:1.0f,e&1?-1.0f:1.0f,-1.0f);
        for(float h = 1.0f; h >= 0; h -= 1.0f/4) {
            for(float delta = 2*PI; delta >= 0; delta -= PI/10)
                v->emplace_back(offset+glm::vec3(r*std::sinf(delta), r*std::cosf(delta), r+(height-2*r)*h));
        }
    }

    // y
    for(int e:{0,1,2,3}) {
        glm::vec3 offset = 0.5f*height*glm::vec3(e&2?-1.0f:1.0f,-1.0f,e&1?-1.0f:1.0f);
        for(float h = 1.0f; h >= 0; h -= 1.0f/4) {
            for(float delta = 2*PI; delta >= 0; delta -= PI/10)
                v->emplace_back(offset+glm::vec3(r*std::cosf(delta), r+(height-2*r)*h, r*std::sinf(delta)));
        }
    }

    // x
    for(int e:{0,1,2,3}) {
        glm::vec3 offset = 0.5f*height*glm::vec3(-1.0f,e&2?-1.0f:1.0f,e&1?-1.0f:1.0f);
        for(float h = 1.0f; h >= 0; h -= 1.0f/4) {
            for(float delta = 2*PI; delta >= 0; delta -= PI/10)
                v->emplace_back(offset+glm::vec3(r+(height-2*r)*h, r*std::sinf(delta), r*std::cosf(delta)));
        }
    }

    for(int offset = 0; offset < 1200; offset += 100){
        for(int j = 0; j < 20; j++) {
            for(int i = 0; i < 4; i++) {
                f->emplace_back(offset+glm::ivec3(i*20+(j+1)%20,i*20+j,(1+i)*20+j));
                f->emplace_back(offset+glm::ivec3((i+1)*20+j,(i+1)*20+(j+1)%20,i*20+(j+1)%20));
            }
        }
    }


    for(int e = 0,offset = 0; e< 8; e++) {
        v->emplace_back(0.5f*height*glm::vec3(e&4?-1.0f:1.0f,e&2?-1.0f:1.0f,e&1?-1.0f:1.0f));
        for(int j = 0; j < 20; j++) {
            offset = 800+80*bool(e&4)+100*(e&3);
            f->emplace_back(offset%100 == 80 ? glm::ivec3((j+1)%20+offset,j+offset,1200+e):glm::ivec3(j+offset,(j+1)%20+offset,1200+e));
            offset = 400+80*bool(e&2)+100*(((e&4)>>1)+(e&1));
            f->emplace_back(offset%100 == 80 ? glm::ivec3((j+1)%20+offset,j+offset,1200+e):glm::ivec3(j+offset,(j+1)%20+offset,1200+e));
            offset =  00+80*bool(e&1)+100*((e&6)>>1);
            f->emplace_back(offset%100 == 80 ? glm::ivec3((j+1)%20+offset,j+offset,1200+e):glm::ivec3(j+offset,(j+1)%20+offset,1200+e));
        }
    }

    return YbMesh::indicesTriMesh<glm::vec3>(v,f);
}

具体的效果如图:

扩展性部分,对于类似MeshLab的坐标轴的坐标尺度效果(对应本文则是坐标轴网的周期纹理效果),实现上可由顶点着色器与面元着色器简单合作完成。

版权声明:本文为xiconxi原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/xiconxi/p/9661595.html