Unity3d中渲染到RenderTexture的原理,几种方式以及一些问题

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

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之间拷贝要考虑到一定的带宽瓶颈。

     什么是FrameBufferObject?

     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渲染使用

  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提供的GetTemporaryReleaseTemporary,它将在内部维护一个池,反复重用一些大小格式一样的rt资源,因为让gpu为你分配一个新的tex其实是要耗时间的。更重要的这里还会调用DiscardContents

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


可以看到虽然RenderTexture这个技术是个普遍使用的技术,但是用好它还是要理解他的底层原理和避免一些使用的问题。

欢迎大家就这个问题进行更多讨论


  • 87
    点赞
  • 189
    收藏
    觉得还不错? 一键收藏
  • 18
    评论
### 回答1: 要将绘制的图像渲染RenderTexture上,可以按照以下步骤进行: 1. 创建一个RenderTexture对象。 2. 将当前的渲染目标设置为RenderTexture对象。 3. 执行绘制操作。 4. 将渲染目标重置回默认值。 以下是一些可能使用的示例代码: ``` // 创建一个RenderTexture对象 RenderTexture renderTexture = new RenderTexture(width, height, depth, format); // 将当前的渲染目标设置为RenderTexture对象 RenderTexture.active = renderTexture; // 执行绘制操作 // 这里可以执行任何绘制操作,比如渲染纹理、绘制模型等等 // 例如,使用Camera来渲染RenderTexture上: camera.targetTexture = renderTexture; camera.Render(); // 将渲染目标重置回默认值 RenderTexture.active = null; ``` 在这个示例,我们创建了一个指定大小、深度和格式的RenderTexture对象。然后,我们将当前的渲染目标设置为这个RenderTexture对象。接着,我们执行任何需要在RenderTexture上绘制的操作,比如使用Camera渲染RenderTexture上。最后,我们将渲染目标重置回默认值。 请注意,这只是一种可能的实现方式,具体的实现方法可能因使用的引擎或框架而异。 ### 回答2: 将画面渲染RenderTexture上的方法有以下几个步骤: 1. 创建一个RenderTexture对象。可以通过调用RenderTexture的构造函数或者使用RenderTexture.Create方法来创建一个RenderTexture对象。可以指定RenderTexture的分辨率、深度位数和附加的各种属性。 2. 将渲染目标设为RenderTexture。通过调用Graphics.SetRenderTarget方法,将渲染目标设为之前创建的RenderTexture对象。这样后续的渲染操作就会将画面渲染RenderTexture上。 3. 进行画面渲染。在RenderTexture设为渲染目标后,所有的绘制操作都会将画面渲染RenderTexture上。可以使用各种图形API(如Unity的Graphics类)提供的绘制函数进行渲染,例如进行模型渲染、粒子效果等。 4. 恢复默认的渲染目标。当画面渲染完成后,需要调用Graphics.SetRenderTarget方法将渲染目标恢复为默认值,通常是屏幕。这样后续的绘制操作就会将画面渲染到屏幕上。 5. 可选地,将RenderTexture用作贴图。通过将RenderTexture传给材质的主纹理属性,可以将RenderTexture作为贴图应用到模型上。这样可以实现将之前渲染的画面作为贴图效果展示在模型上。 总结起来,将画面渲染RenderTexture上的方法就是通过创建和设置RenderTexture为渲染目标,然后进行画面渲染,最后恢复默认的渲染目标。这样可以实现将渲染结果保存到RenderTexture,以供后续使用。 ### 回答3: 将画面渲染RenderTexture上的方法有多种,以下是其一种常用的方法: 首先,我们需要创建一个RenderTexture对象。可以使用UnityRenderTexture.Create()函数进行创建。在创建时,需要指定RenderTexture的宽度、高度、深度和颜色格式等参数。 然后,需要将摄像机的目标纹理设置为刚刚创建的RenderTexture。可以通过设置摄像机的targetTexture属性来实现,将其指定为RenderTexture对象。 接下来,可以使用摄像机进行渲染。将目标摄像机设置为活动摄像机,然后调用摄像机的Render()函数进行渲染。在渲染过程,将会将画面渲染RenderTexture上。 最后,可以将RenderTexture作为纹理应用到游戏对象的材质上,或者将其保存为文件等。可以使用RenderTexture的GetReadableTexture()函数获取可以读取的纹理对象,然后将其应用到需要显示的对象上。 需要注意的是,渲染RenderTexture上可能会对性能产生一定影响,因此应该根据实际需求进行调优和优化,以提高游戏的性能和流畅度。同时,在不需要使用RenderTexture时,需要及时释放和销毁相关的资源,以避免内存泄漏等问题的产生。 综上所述,将画面渲染RenderTexture上的方法包括创建RenderTexture对象、将摄像机的目标纹理设置为RenderTexture、使用摄像机进行渲染、将RenderTexture应用到游戏对象的材质上等步骤。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值