原理想了解更多OpenGLES的,请移步OpenGLES相关文章目录,本文使用的代码在https://github.com/SquarePants1991/ARKit分支中的OpenGLESLearn.git。iOS11引入了新的框架ARKit,通过ARKit和SceneKit可以轻松创建ARApp。Apple也提供了AR的基础应用框架,你可以从中直接开始你的ARApp的开发。不过,本系列文章将使用OpenGLES为ARKit提供渲染支持。接下来,我们先来了解一下ARKit的理论知识。AR的基本概念AR最基本的概念是将虚拟计算机图形与真实环境相结合的技术。有很多方法可以实现这种技术。使用2D或3D图形修饰人脸,常见于一些相机、视频类APP,主要使用人脸识别和追踪技术。基于标记的3D模型放置,例如基于AR的故事书、阴阳师的生命呼唤。标记可以是用黑框包裹的简单标记,也可以是复杂图片的特征点训练数据。如果您有兴趣,请前往ARToolKit,这是一个主要用于基于标记的AR的开源AR框架。ARToolkit6Beta最近发布了,不知道有没有新功能开放。跟踪真实环境的特征点,计算真实相机在真实环境中的位置。所谓特征点就是图片中灰度变化比较剧烈的位置。因此,要想计算得更准确、更稳定,就需要在真实环境中有更丰富的颜色变化。ARKit使用此原理进行相机定位。WorldTracking是AR实现中最重要的部分,通过跟踪现实世界的特征点,计算出真实的摄像机位置并将其应用于3D世界中的虚拟摄像机。计算结果的准确性直接影响渲染结果。ARKit使用ARSession来管理整个AR处理管道,包括相机位置的计算。#pragmamake-ARControl-(void)setupAR{if(@available(iOS11.0,*)){self.arSession=[ARSessionnew];self.arSession.delegate=self;}}-(void)runAR{if(@可用(iOS11.0,*)){ARWorldTrackingSessionConfiguration*config=[ARWorldTrackingSessionConfigurationnew];config.planeDetection=ARPlaneDetectionHorizo??ntal;[self.arSessionrunWithConfiguration:config];}}-(void)pauseAR{if(@available(iOS11.0,*)){[self.arSessionpause];}}ARSession的使用方法非常简单。初始化、设置delegate、开启ARSession,需要传入一个配置ARWorldTrackingSessionConfiguration。ARWorldTrackingSessionConfiguration表示AR系统会追踪现实世界的特征点,并计算相机位置。Apple也可能会在未来发布诸如ARMarkerTrackingSessionConfiguration之类的配置来识别跟踪标记。开启ARSession后,会启动摄像头,通过传感器感知手机的位置。借用WWDC的一张图。ARSession将摄像头捕捉到的视频流和位置信息进行合成,生成一系列连续的ARFrame。-(void)session:(ARSession*)sessiondidUpdateFrame:(ARFrame*)frame{...}每个ARFrame都包含从摄像头抓取的图片,摄像头位置等信息。在这个方法中我们需要绘制由相机。根据相机位置等信息绘制3D对象等。平面检测ARKit提供了另一个很酷的功能,可以检测真实世界的平面,并提供一个描述平面位置、大小、方向等的ARPlaneAnchor对象。-(void)runAR{if(@available(iOS11.0,*)){ARWorldTrackingSessionConfiguration*config=[ARWorldTrackingSessionConfigurationnew];config.planeDetection=ARPlaneDetectionHorizo??ntal;[self.arSessionrunWithConfiguration:config];}}以上config.planeDetection=ARPlaneDetectionHorizo??ntal;将检测平面类型设置为水平。但是,这是当前唯一可用的选项。如果ARKit检测到平面,它会通过委托中的方法-(void)session:(ARSession*)sessiondidAddAnchors:(NSArrayHitTestHitTest可以方便地在检测到的平面上放置物体。当你点击屏幕时,使用HitTest可以检测你点击了哪些平面,并提供ARAnchor来设置放置物体的位置[framehitTest:CGPointMake(0.5,0.5)types:ARHitTestResultTypeExistingPlane];使用ARFrame的hitTest方法,第一个传入的点值范围从(0,0)到(1,1),第二个参数表示可以检测到哪些物体,可以检测的物体如下ARHitTestResultTypeFeaturePoint,根据最近的特征点检测的连续曲面ARHitTestResultTypeEstimatedHorizo??ntalPlane,平面垂直于重力的计算方式不准确ARHitTestResultTypeExistingPlane,已经检测到的平面,在检测时忽略了平面本身的大小,将其视为一个inf初始平面。ARHitTestResultTypeExistingPlaneUsingExtent,已经检测到的平面,在检测尺寸的时候会考虑平面本身的尺寸。如果检测成功,返回NSArray*,ARHitTestResult包含检测类型、交点距离、平面的ARAnchor。请注意,仅当检测到ARHitTestResultTypeExistingPlane和ARHitTestResultTypeExistingPlaneUsingExtent时,ARAnchor才可用。这四种检测类型可以通过|同时存在,比如ARHitTestResultTypeEstimatedHorizo??ntalPlane|ARHitTestResultTypeExistingPlane。光照强度调节ARKit还提供了检测光照强度的功能,主要是让3D模型的光照强度与环境光照强度保持一致。ARFrame中有一个lightEstimate变量,如果光强检测成功,就会有一个值。该值是ARLightEstimate类型,它只包含一个变量ambientIntensity。在3D光照模型中,它对应于环境光,它的值是从0到2000。当用OpenGL渲染时,你可以用这个值来调整光照模型中的环境光强度。ARKit的理论知识到这里就差不多结束了,下篇文章将介绍如何使用OpenGLES渲染ARFrame中的内容。用于实现本文的代码在https://github.com/SquarePants1991/OpenGLESLearn.git的ARKit分支中。本文使用的OpenGL基础代码来自OpenGLES系列,具有渲染几何、纹理等基础功能,实现细节不再赘述。集成ARKit的关键代码在ARGLBaseViewController中。我们来看看它的代码。处理ARFrame-(void)session:(ARSession*)sessiondidUpdateFrame:(ARFrame*)frame{//同步YUV信息到yTexture和uvTextureCVPixelBufferRefpixelBuffer=frame.capturedImage;GLsizeiimageWidth=(GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer,0);GLsizeiimageHeight=(GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer,0);void*baseAddress=CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,0);glBindTexture(GL_TEXTURE_2D,self.yTexture);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,imageWidth,imageHeight,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,baseTexturel);(GL_TEXTURE_2D,0);imageWidth=(GLsizei)CVPixelBufferGetWidthOfPlane(pixelBuffer,1);imageHeight=(GLsizei)CVPixelBufferGetHeightOfPlane(pixelBuffer,1);void*laAddress=CVPixelBufferGetBaseAddressOfPlane(pixelBuffer,1);glBindTexture(GL_TEXTURE_2D,self.uvTexture);glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE_ALPHA,imageWidth,imageHeight,0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,laAddress);glBindTexture(GL_TEXTURE_2D,0);self.videoPlane.yuv_yTexture=self.yTexture;self.videoPlane.yuv_uvTexture=self.uvTexture;[selfsetupViewport:CGSizeMake(imageHeight,imageWidth)];//同步相机matrix_float4x4cameraMatrix=matrix_invert([frame.cameratransform]);GLKMatrix4newCameraMatrix=GLKMatrix4Identity=;0;col<4;++col){for(introw=0;row<4;++row){newCameraMatrix.m[col*4+row]=cameraMatrix.columns[col][row];}}self.cameraMatrix=newCameraMatrix;GLKVector3forward=GLKVector3Make(-self.cameraMatrix.m13,-self.cameraMatrix.m23,-self.cameraMatrix.m33);GLKMatrix4rotationMatrix=GLKMatrix4MakeRotation(M_PI/2,forward.x,forward.y,forward.z);self.cameraMatrix=GLKMatrix4Multiply(rotationMatrix,newCameraMatrix);}上面的代码展示了如何处理ARKit捕获的ARFrame。ARFrame的capturedImage存储的是摄像头拍摄的图片信息。类型是CVPixelBufferRef。图片信息默认格式为YUV,由两个Planes存储,也可以理解为两张图片。一种格式是Y(Luminance),保存亮度信息,另一种是UV(Chrominance,Chroma),保存色度和密度。我们需要将这两个图像绑定到不同的纹理上,然后使用Shader中的算法将YUV转换为RGB。下面是处理贴图的FragmentShader,使用公式进行颜色转换。precisionhighpfloat;varyingvec3fragNormal;varyingvec2fragUV;uniformfloatelapsedTime;uniformmat4normalMatrix;uniformsampler2DyMap;uniformsampler2DuvMap;voidmain(void){vec4Y_planeColor=texture2D(yMap,fragUV);vec4CbCr_planeColor=texture2D(uvMap,fragUV);浮动,YCb,CatYCb;猫=Y_planeColor.r*255.0;Cb=CbCr_planeColor.r*255.0-128.0;Cr=CbCr_planeColor.a*255.0-128.0;R=1.402*Cr+Y;G=-0.344*Cb-0.714*Cr+Y;B=1.772*Cb+Y;vec4videoColor=vec4(R/255.0,G/255.0,B/255.0,1.0);gl_FragColor=videoColor;}对纹理进行处理绑定后,为了保证不同屏幕下纹理不相等sizes比拉伸,所以重新计算视口[selfsetupViewport:CGSizeMake(imageHeight,imageWidth)];。接下来,将ARKit计算的相机变换分配给self.cameraMatrix。注意ARKit抓取的图片需要旋转90度才能正常显示,所以在设置Viewport的时候故意把宽高调反了,最后旋转了相机。VideoPlaneVideoPlane是显示视频写入的几何体,它可以接收两个贴图,Y和UV。@interfaceVideoPlane:GLObject@property(assign,nonatomic)GLuintyuv_yTexture;@property(assign,nonatomic)GLuintyuv_uvTexture;-(instancetype)initWithGLContext:(GLContext*)context;-(void)update:(NSTimeInterval)timeSinceLastUpdate;-(void)draw:(GLContext*)glContext;@end...-(void)draw:(GLContext*)glContext{[glContextsetUniformMatrix4fv:@"modelMatrix"value:self.modelMatrix];boolcanInvert;GLKMatrix4normalMatrix=GLKMatrix4InvertAndTranspose(self.modelMatrix,&canInvert);[glContextsetUniformMatrix4fv:@"normalMatrix"值:canInvert?normalMatrix:GLKMatrix4Identity];[glContextbindTextureName:self.yuv_yTextureto:GL_TEXTURE0uniformName:@"yMap"];[glContextbindTextureName:self.yuv_TriangTextureto:GL_TEXTURE0uniformName:@"yMap"];[self.yuv_TriangTextureto:GL_TEXTURE0uniformName:@"ConglivWles"vaovertexCount:6];}其他功能很简单,就是画一个正方形,最后配合显示视频的Shader渲染YUV格式的数据。透视投影矩阵可以得到ARFrame中渲染所需的纹理和相机矩阵。除了这些,匹配真实相机的透视投影矩阵也是必须的。它可以使渲染的3D对象的透视看起来自然。-(无效)会话:(ARSession*)sessioncameraDidChangeTrackingState:(ARCamera*)相机{matrix_float4x4projectionMatrix=[cameraprojectionMatrixWithViewportSize:self.viewport.sizeorientation:UIInterfaceOrientationPortraitzNear:0.1zFar:1000];GLKMatrix4newWorldProjectionMatrix=GLKMatrixforcol<4Identity;++col){for(introw=0;row<4;++row){newWorldProjectionMatrix.m[col*4+row]=projectionMatrix.columns[col][row];}}self.worldProjectionMatrix=newWorldProjectionMatrix;上面的代码演示了如何通过ARKit获取3D透视投影矩阵。有了透视投影矩阵和相机矩阵,就可以很方便的使用OpenGL渲染物体。-(void)glkView:(GLKView*)viewdrawInRect:(CGRect)rect{[superglkView:viewdrawInRect:rect];[self.objectsenumerateObjectsUsingBlock:^(GLObject*obj,NSUIntegeridx,BOOL*stop){[obj.contextactive];[obj.contextsetUniform1f:@"elapsedTime"值:(GLfloat)self.elapsedTime];[obj.contextsetUniformMatrix4fv:@"projectionMatrix"值:self.worldProjectionMatrix];[obj.contextsetUniformMatrix4fv:@"cameraMatrix"值:self.cameraMatrix];[obj.contextsetUniform3fv:@"lightDirection"value:self.lightDirection];[objdraw:obj.context];}];}本文主要介绍了OpenGLES渲染ARKit的基本思想,没有描述过多的技术细节OpenGLES。有兴趣的可以直接cloneGithub上的代码进行更深入的理解。
