metal的基础知识入门,首推Metal By Example系列:http://metalbyexample.com/。博主的相关文章,主要给出工程实际遇到的典型问题及其解决方案。
(a)离屏彩色纹理 (b)离屏深度纹理
本节源码:https://github.com/sjy234sjy234/Learn-Metal/tree/master/OffScreenRendering。这一次,主要介绍利用metal做离屏渲染,同时对此前提出的工程化metal框架进行了改进,以适用于更复杂的渲染场景。源码同样实现了一个三维立方体线框图的渲染,与之前不同的是这里的渲染有一个中间步骤,首先是将结果渲染在一张彩色纹理和一张深度纹理中,最后将得到的离屏纹理再渲染到视图中,从而实现了metal离屏渲染的基本步骤。其中图(a)是彩色纹理,图(b)是深度纹理的可视化效果,这一预览过程也可以加深之前三维模型线框图绘制章节的原理的理解:https://mp.csdn.net/postedit/82350452。
首先是关于工程框架的设计,此前介绍了把渲染的对象分成context、view、renderer三个部分进行渲染管理:https://blog.csdn.net/sjy234sjy234/article/details/81812029。在此基础上,增加另外一个部分——encoder,并将原先renderer中pipeline、buffer的管理,以及对MTLCommandBuffer的encode的操作分流到各个encoder类中。而原来的renderer,只保留对layer、render texture,以及MTLCommandBuffer的分配和管理操作。在具体业务场景中,完成一次视图渲染可能需要经过很多步的渲染操作,每一步的操作的具体编码由一个个的encoder类去完成,而renderer负责把所有的encoder汇总在一起,进行封装。此框架实际上是参考了Metal Performance Shader里的接口封装、调用方式而设计的。
在本节的demo中,一共有两步渲染操作:1)离屏渲染线框图到1张彩色纹理和1张深度纹理中;2)将离屏得到的纹理渲染到视图。因此需要两个encoder类:FrameRendererEncoder和TextureRendererEncoder分别执行两次渲染操作。然后用一个render类:FrameRenderer来对两步操作进行封装管理,并提供api给上层的ViewController进行调用。这样进行封装以后,在ViewController中的渲染操作将变得非常简单清晰:
- (void)redraw
{
//mvp matrix
m_viewTransform = m_orbitControl.getTransform();
const simd::float4x4 mvpTransform = m_proTransform * m_viewTransform * m_modelTransform;
[_frameRenderer renderWithMvpMatrix: mvpTransform];
}
而在FrameRenderer类中,井井有条的安排两个encoder去执行具体的渲染编码:
- (void)renderWithMvpMatrix: (const simd::float4x4)mvpTransform
{
//new commander buffer
id<MTLCommandBuffer> commandBuffer = [_metalContext.commandQueue commandBuffer];
commandBuffer.label = @"FrameRendererCommand";
//encode offscreen frame render process
[_frameRendererEncoder encodeToCommandBuffer: commandBuffer dstColorTexture: _colorTexture dstDepthTexture: _depthTexture mvpMatrix: mvpTransform];
//encode drawable render process
id<CAMetalDrawable> drawable = [_layer nextDrawable];
if(drawable)
{
[_textureRendererEncoder encodeToCommandBuffer: commandBuffer sourceTexture: _colorTexture destinationTexture: drawable.texture];
// [_textureRendererEncoder encodeToCommandBuffer: commandBuffer sourceTexture: _depthTexture destinationTexture: drawable.texture];
[commandBuffer presentDrawable:drawable];
}
//commit commander buffer
[commandBuffer commit];
}
FrameRenderer类的renderWithMvpMatrix方法中,首先进行commandBuffer的分配,然后调用FrameRendererEncoder的encode方法进行离屏渲染操作的编码,再调用TextureRendererEncoder的encode方法进行纹理渲染操作的编码,最后执行commit执行渲染操作,图层layer的管理也在FrameRenderer类中完成。注释掉的那行代码可尝试运行,会将离屏得到的深度图绘制到视图中,如图(b)所示。
该项目同时演示了给定一个视角和三维模型,ray casting得到深度图的GPU渲染实现方法。此外,在metal中,MTLBuffer和MTLTexture的内存在是可以直接访问甚至直接改写的。而内存中存储的内容也与GPU管线中的数据同步,非常的方便,效率也非常的高。