概述
metal的基础知识入门,首推Metal By Example系列:http://metalbyexample.com/。博主的相关文章,主要给出工程实际遇到的典型问题及其解决方案。
本节源码:https://github.com/sjy234sjy234/Learn-Metal/tree/master/TextureRenderer。这一次,主要介绍利用metal做视频流的实时渲染。源码实现了从前置摄像头获取实时的视频流,并且全屏渲染显示到一个视图中。
首先,是ios的调用相机session的基础知识,建议自行搜索了解,或者直接看博主的封装。博主这里简单封装了一个FrontCamera类,只支持portrait方向的preset为AVCaptureSessionPresetHigh的前置摄像头的session调用。在ViewController中初始化FrontCamera实例,重写FrontCamera的代理,将获得实时刷新的视频流:
//FrontCameraDelegate
- (void)didOutputVideoBuffer:(CVPixelBufferRef)videoPixelBuffer
{
if(videoPixelBuffer)
{
[self.textureRenderer draw: [_metalContext textureFromPixelBuffer: videoPixelBuffer]];
}
}
可以看到,实时的视频流格式是(CVPixelBufferRef)videoPixelBuffer,不能直接进行绘制,因此需要实现一个函数由videoPixelBuffer转化获取id<MTLTexture>格式的纹理texture,该方法被封装在MetalContext类中:
- (id<MTLTexture>) textureFromPixelBuffer:(CVPixelBufferRef)videoPixelBuffer
{
id<MTLTexture> texture = nil;
{
size_t width = CVPixelBufferGetWidth(videoPixelBuffer);
size_t height = CVPixelBufferGetHeight(videoPixelBuffer);
MTLPixelFormat pixelFormat = MTLPixelFormatBGRA8Unorm;
CVMetalTextureCacheRef textureCache;
CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, _device, nil, &textureCache);
CVMetalTextureRef metalTextureRef = NULL;
CVReturn status = CVMetalTextureCacheCreateTextureFromImage(NULL, textureCache, videoPixelBuffer, NULL, pixelFormat, width, height, 0, &metalTextureRef);
if(status == kCVReturnSuccess)
{
texture = CVMetalTextureGetTexture(metalTextureRef);
CFRelease(metalTextureRef);
CFRelease(textureCache);
}
}
return texture;
}
由视频流转化得到纹理texture以后,传递给TextureRender类的实例绘制即可。而TextureRender类,封装了把一个texture绘制到整个MetalView上的方法,实质上就是把整个视图区域当成一个矩形,将纹理texture做为贴图贴上即可。纹理贴图的概念:https://github.com/sjy234sjy234/sample-code/tree/master/objc/06-Texturing。下面只给出TextureRender的shader代码:
#include <metal_stdlib>
using namespace metal;
struct TextureVertex
{
float4 position [[position]];
float2 texCoords;
};
vertex TextureVertex texture_vertex_main(constant TextureVertex *vertices [[buffer(0)]],
uint vid [[vertex_id]])
{
return vertices[vid];
}
fragment float4 texture_fragment_main(TextureVertex vert [[stage_in]],
texture2d<float> videoTexture [[texture(0)]],
sampler samplr [[sampler(0)]])
{
float3 texColor = videoTexture.sample(samplr, vert.texCoords).rgb;
float2 texCoords=vert.texCoords;
float2 texCenter={0.5,0.5};
if(distance(texCoords,texCenter)<=0.2859)
{
return float4(texColor, 1);
}
else
{
// return float4(texColor/3, 1);
return float4(texColor, 1);
}
}
大家注意看一下倒数第二行的注释,可以尝试运行一下,可以对metal的纹理坐标值了解的更多一些,metal的纹理坐标值与openGL是有区别的,在fragment shader中它的范围是[0, 1]。
小结:
(1)视频流和纹理的渲染是非常简单的一件事情,之所以当作一节专门介绍,是因为它是实现离屏渲染的最后一个环节。在做渲染时,很多情况下,我们需要对三维渲染做图像后处理。这个时候就需要将中间结果先离屏渲染到一张纹理中,对纹理做一系列图像后处理,最后才把该纹理渲染到视图中。本文介绍的就是这最后一步的操作。此外,除了相机session的实时获取的视频流,无论是从I/O方式读取的文件视频流,还是从网络上实时获取的数据视频流,都是可以用本节的方式进行实时渲染的。
(2)博主封装的FrontCamera相机设备类,还提供portrait方向的preset为AVCaptureSessionPreset640x480的真实感深度相机的session调用,只适用于iphone X系列。这是博主在ios平台上实现KinectFusion算法时用到的,需要调用iphone X的真实感深度相机的小伙伴可以用来参考。顺便吐槽一下iphone X真实感深度相机调用的槽点,官方文档没有给出相机session调用的demo,需要开发者自行摸索。想要获取实时的iphone X的实时深度帧,目前有两种方式:一种是使用ARSession的高层封装,官方给出了demo的,大家自行检索关键字ARKitFaceExample,这种方式的缺点是只能获取15fps的帧率,而且镜头的利用率似乎没有达到100%。另一种是使用相机session的调用,用法可以参考博主的FrontCamera类,这种方式的优点是能获取30fps的帧率,镜头利用率高,缺点是获取30fps的帧率的同时,获取的视频流的分辨率较低只有640x480,当然可以修改preset,但是其他的preset值要么不支持深度相机,要么只支持15fps的深度帧率,大家可以自己尝试修改preset。
最后
以上就是玩命雪碧为你收集整理的学习ios Metal(6)—前置相机视频流的实时渲染的全部内容,希望文章能够帮你解决学习ios Metal(6)—前置相机视频流的实时渲染所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复