浅谈Unity中 RenderTexture 实现原理(转)

本文中大部分例子将按照Opengles的实现来解释ide

1.RenderTexture是什么

 

     在U3D中有一种特殊的Texture类型,叫作RenderTexture,它本质上一句话是将一个FrameBufferObjecrt链接到一个server-side的Texture对象。函数

     什么是server-sider的texture?优化

     在渲染过程当中,贴图最开始是存在cpu这边的内存中的,这个贴图咱们一般称为client-side的texture,它最终要被送到gpu的存储里,gpu才能使用它进行渲染,送到gpu里的那一份被称为server-side的texture。这个tex在cpu和gpu之间拷贝要考虑到必定的带宽瓶颈。spa

     什么是FrameBufferObject?server

     FrameBuffer就是gpu里渲染结果的目的地,咱们绘制的全部结果(包括color depth stencil等)都最终存在这个这里,有一个默认的FBO它直接连着咱们的显示器窗口区域,就是把咱们的绘制物体绘制到显示器的窗口区域。可是现代gpu一般能够建立不少其余的FBO,这些FBO不链接窗口区域,这种咱们建立的FBO的存在目的就是容许咱们将渲染结果保存在gpu的一块存储区域,待以后使用,这是一个很是有用的东西。对象

    当渲染的结果被渲染到一个FBO上后,就有不少种方法获得这些结果,咱们能想一想的使用方式就是把这个结果做为一个Texture的形式获得,一般有这样几种方式获得这个贴图:接口

    a) 将这个FBO上的结果传回CPU这边的贴图,在gles中的实现通常是ReadPixels()这样的函数,这个函数是将当前设为可读的FBO拷贝到cpu这边的一个存储buffer,没错若是当前设为可读的FBO是那个默认FBO,那这个函数就是在截屏,若是是你本身建立的FBO,那就把刚刚绘制到上面的结果从gpu存储拿回内存。内存

   b)   将这个FBO上的结果拷贝到一个gpu上的texture,在gles中的实现通常是CopyTexImage2D(),它通常是将可读的FBO的一部分拷贝到存在于gpu上的一个texture对象中,直接考到server-sider就意味着能够立刻被gpu渲染使用ci

  c)  将这个fbo直接关联一个gpu上的texture对象,这样就等于在绘制时就直接绘制到这个texure上,这样也省去了拷贝时间,gles中通常是使用FramebufferTexture2D()这样的接口。资源

  

  那么unity的RenderTexture正是这种c)方式的一种实现,它定义了在server-side的一个tex对象,而后将渲染直接绘制到这个tex上。

  这有什么用?

  咱们能够将场景的一些渲染结果渲染到一个tex上,这个tex能够被继续使用。例如,汽车的后视镜,后视镜就能够贴一个rendertex,它是从这个视角的摄像机渲染而来。

  咱们还能够利用这个进行一些图像处理操做,传统的图像处理在cpu中一个for循环,一次处理一个像素,若是咱们渲染一个四方形,而后把原图当成tex传入进去,写一个fragment shader,将渲染的结果渲染到一个rendertex上,那么rendertex上的东西就是图像处理的结果,unity中的一些图像后处理效果(如模糊,hdr等)就是这样作的。

 

2.渲染到RenderTexture的几种方式

 

 1.在assets里建立一个RenderTexture,而后将其附给一个摄像机,这样这个摄像机实时渲染的结果就都在这个tex上了。

 2.有的时候咱们想人为的控制每一次渲染,你能够将这个摄像机disable掉,而后手动的调用一次render。

 3. 有的时候咱们想用一个特殊的shader去渲染这个RenderTexture,那能够调用cam的RenderWithShader这个函数,它将使用你指定的shader去渲染场景,这时候场景物体上原有的shader都将被自动替换成这个shader,而参数会按名字传递。这有什么用?好比我想获得当前场景某个视角的黑白图,那你就能够写个渲染黑白图的shader,调用这个函数。(这里还有一个replacement shader的概念,很少说,看下unity文档)

 4. 咱们还能够不用本身在assets下建立rendertexture,直接使用Graphics.Blit(src, target, mat)这个函数来渲染到render texture上,这里的的target就是你要绘制的render texrture,src是这个mat中须要使用的_mainTex,能够是普通tex2d,也能够是另外一个rendertex,这个函数的本质是,绘制一个四方块,而后用mat这个材质,用src作maintex,而后先clear为black,而后渲染到target上。这个是一个快速的用于图像处理的方式。咱们能够看到UNITY的不少后处理的一效果就是一连串的Graphics.Blit操做来完成一重重对图像的处理,若是在cpu上作那几乎是会卡死的。

    

3.从rendertex获取结果

 

 大部分状况咱们渲染到rt就是为了将其做为tex继续给其余mat使用。这时候咱们只需把那个mat上调用settexture传入这个rt就行,这彻底是在gpu上的操做。

但有的时候咱们想把它拷贝回cpu这边的内存,好比你想保存成图像,你想看看这个图什么样,由于直接拿着rt你并不能获得它的每一个pixel的信息,由于他没有内存这一侧的信息。Texture2d之因此有,是由于对于选择了read/write属性的tex2D,它会保留一个内存这边的镜像。这种考回就是1部分写的a)方式,把rt从gpu拷贝回内存,注意这个操做不是效率很高。copy回的方法一般是这样的

            Texture2D uvtexRead = new Texture2D()

            RennderTexture currentActiveRT = RenderTexture.active;
            // Set the supplied RenderTexture as the active one
            RenderTexture.active = uvTex;
            uvtexRead.ReadPixels(new Rect(0, 0, uvTexReadWidth, uvTexReadWidth), 0, 0);
            RenderTexture.active = currentActiveRT;

上面这段代码就是等于先把当前的fbo设为可读的对象,而后调用相关操做将其读回内存。

4.其余的一些问题

 

1.rendertexture的格式,rt的格式和普通的tex2D的格式并非一回事,咱们查阅文档,看到rt的格式支持的有不少种,最基本的ARGB32是确定支持的,不少机器支持ARRBHALF或者ARGBFLOAT这样的格式,这种浮点格式是颇有用的,想象一下你想把场景的uv信息保存在一张图上,你要保存的就不是256的颜色,而是一个个浮点数。可是使用前必定要查询当前的gpu支持这种格式

2.若是你想从RenderTexture拷贝回到内存,那么rt和拷贝回来的tex的格式必须匹配,且必须是rgba32或者RGBA24这种基本类型,你把float拷贝回来应该是不行的

3.rendertexture的分配和销毁,若是你频繁的要new一个rt出来,那么不要直接new,而是使用RenderTexture提供的GetTemporary和ReleaseTemporary,它将在内部维护一个池,反复重用一些大小格式同样的rt资源,由于让gpu为你分配一个新的tex实际上是要耗时间的。更重要的这里还会调用DiscardContents

4 DiscardContents()这个rendertex的接口很是重要,好的习惯是你应该尽可能在每次往一个已经有内容的rt上绘制以前老是调用它的这个DiscardContents函数,大体获得的优化是,在一些基于tile的gpu上,rt和一些tile的内存之间要存在着各类同步, 若是你准备往一个已经有内容的rt上绘制,将触发到这种同步,而这个函数告诉gpu这块rt的内容不用管他了,我反正是要从新绘制,这样就避免了这个同步而产生的巨大开销。总之仍是尽可能用GetTemporray这个接口吧,它会自动为你处理这个事情

相关文章
相关标签/搜索