学习ios Metal(5)—三维模型线框图WireFrame的绘制

       metal的基础知识入门,首推Metal By Example系列:http://metalbyexample.com/。博主的相关文章,主要给出工程实际遇到的典型问题及其解决方案。

        学习ios Metal(5)—三维模型线框图WireFrame的绘制_第1张图片                    学习ios Metal(5)—三维模型线框图WireFrame的绘制_第2张图片                     学习ios Metal(5)—三维模型线框图WireFrame的绘制_第3张图片                     学习ios Metal(5)—三维模型线框图WireFrame的绘制_第4张图片

              (a)无遮挡                          (b)mesh+frame                           (c)遮挡效果                            (d)模型渲染

        本节源码:https://github.com/sjy234sjy234/Learn-Metal/tree/master/MeshFrame。

        此前,博主介绍了宽度可调的线条的绘制:https://blog.csdn.net/sjy234sjy234/article/details/81837370,但是没有介绍如何解决线条之间的三维遮挡关系,因此不能正确的绘制出三维模型的wireframe线框图。如图(a)所示,是利用之前的方法直接绘制的一个没有遮挡效果的立方体的线框图。

        这里采用两次渲染,来解决这个问题。第一次渲染先将原三维模型绘制到视图中,但是fragment shader里面不修改颜色,具体的做法是返回颜色时返回纯透明的颜色,这样操作的结果是不渲染颜色但是渲染了深度:

vertex Vertex frameMesh_vertex_main(device Vertex *vertices [[buffer(0)]],
                               constant MvpTransform *mvpTransform [[buffer(1)]],
                               uint vid [[vertex_id]])
{
    Vertex vertexOut;
    vertexOut.position = mvpTransform->matrix * vertices[vid].position;
    return vertexOut;
}

fragment half4 frameMesh_fragment_main(Vertex vertexIn [[stage_in]])
{
    return {1.0,1.0,1.0,0.0};
}

        在第一次的深度渲染操作以后,再进行线条的渲染,就能得到有遮挡效果的线框图了。这部分代码参考此前的博文。

        但是这里有三个注意事项。

        1、需要根据已知三维模型的vertex和index的信息,重新生成需要绘制线条的index,具体的做法是把每个三角形转化为对应的三条线段,在FrameRenderer类的setupFrameWithVertex方法中实现,具体做法如下:

    //line index
    uint32_t *lineIndices = new uint32_t[faceNum * 6];
    for(int i = 0; i < faceNum; ++i)
    {
        lineIndices[6 * i] = indices[3 * i];
        lineIndices[6 * i + 1] = indices[3 * i + 1];
        lineIndices[6 * i + 2] = indices[3 * i + 1];
        lineIndices[6 * i + 3] = indices[3 * i + 2];
        lineIndices[6 * i + 4] = indices[3 * i + 2];
        lineIndices[6 * i + 5] = indices[3 * i];
    }
    _lineIndexBuffer = [_metalContext.device newBufferWithBytes: lineIndices
                                                         length: faceNum * 6 * 4
                                                        options:MTLResourceOptionCPUCacheModeDefault];
    delete[] lineIndices;

       

        2、必须预先分配一个深度texture,以支持深度渲染和深度值的保留,如果不指定,只能得到图(b)的效果,也就是说第一次的深度渲染操作并不起作用;

        3、在进行线条的渲染时,vertex shader传给frame shader的position的z值,需要减去一个很小的正值,以避免线条被原来的模型部分遮挡,读者可以尝试去掉这个操作看一下渲染效果,详见源码的frame.metal文件,博主这里减去的值是0.001:

outVertex.position.z -= 0.001;

         综上,一个三维模型线框图WireFrame的绘制就完成了,如图(c)(d)所示。

         补充说明,由于视图的宽高比不一定是1/1,因此会导致线宽在横纵两个方向上的宽度不一致,只要传入宽高比的值到shader中,对线条的宽度的x值进行等比例修正即可,源码已经更正:

v1.x /= whRatio->ratio;

 

你可能感兴趣的:(Metal)