渲染到纹理(Render To Texture, RTT)详解

RTT是现在很多特效里面都会用到的一项很基本的技术,实现起来很简单,也很重要。但是让人不解的是网上搜索了半天只找到很少的文章说这个事儿,不知道是因为太简单还是因为这项技术已经出现很长时间了。总之我是在摸索这个东西的时候绕了不少弯子。现在把具体的实现方法写下来。

什么是纹理

熟悉DX的兄弟们都知道什么叫纹理了,这里简单介绍一下,先看看现实生活中的例子吧,其实纹理的例子比比皆是,比如地板,墙面都是纹理。在图形学中,纹理主要是为了增强场景的真实感,如果你想绘制一个地面,简单一点可以直接使用一个矩形,稍微复杂一点可以用三角形网格,再复杂一点可以使用地面纹理,有了纹理以后真实感明显增强了。DX中的纹理映射其实就是对现实生活中纹理的模拟,D3D中有专门的数据结构来管理纹理。

渲染到纹理

常规的渲染操作都是直接将场景呈现到backbuffer中的,backbuffer说白了其实就是一个表面,再说白了就是一块内存,场景通过绘制函数载入显存后,再通过Present函数送至显示器。那么为什么还要渲染到纹理呢?这是为了实现一些特殊的效果,比如常见的环境映射,简单的说,想象你有一个光滑的球体,它应该是可以反射周围环境的,这就是环境映射。

实现步骤

上面说了常规的渲染操作是将场景送至backbuffer,而backbuffer实际上是一个Surface,而纹理恰恰又包含了Surface,所以我们只需要取得纹理的Surface,其次将场景送至这个Surface,最后再把这个纹理渲染到backbuffer中即可。举个例子,假设你要在一面墙壁上画一幅画,你有两种方法

1 直接在墙上画,这个很好理解,就对应常规的backbuffer渲染。

2 先将画绘制在纸上,然后将纸贴到墙上,这就对应渲染到纹理的过程。

这里墙壁相当于backbuffer,而纸张相当于纹理的Surface,在纸上作画相当于渲染到纹理,把纸贴到墙上相当于把纹理渲染到backbuffer,希望大家没有迷糊就好。具体的步骤如下

1 创建纹理并获得纹理的表面(Surface)

2 向纹理的表面渲染场景

3 渲染纹理本身

代码

1.  声明变量

[html] view plaincopyprint?
  1. LPDIRECT3DTEXTURE9 pRenderTexture = NULL; // 目标纹理
  2. PDIRECT3DSURFACE9 pRenderSurface = NULL,pBackBuffer = NULL, pTempSurface;
  3. // pRenderSurface是pRenderTexture 对应的Surface
  4. // pBackBuffer用于保存原来的Render Target
     LPDIRECT3DTEXTURE9 pRenderTexture = NULL; // 目标纹理LPDIRECT3DSURFACE9 pRenderSurface = NULL,pBackBuffer = NULL, pTempSurface;// pRenderSurface是pRenderTexture 对应的Surface// pBackBuffer用于保存原来的Render Target

2.创建一个纹理作为渲染目标(Render Target)


[html] view plaincopyprint?
  1. //注意这里的第三个参数必须为D3DUSAGE_RENDERTARGET
  2. //第四个参数决定纹理的格式,不同的场景会要求不同的格式
  3. pd3dDevice->CreateTexture( TEX_WIDTH,TEX_HEIGHT,1,D3DUSAGE_RENDERTARGET,
  4. D3DFMT_R5G6B5,D3DPOOL_DEFAULT,&pRenderTexture,NULL);
  5. //获得pRenderTexture对应的Surface
  6. pRenderTexture->GetSurfaceLevel(0,&pRenderSurface);
 //注意这里的第三个参数必须为D3DUSAGE_RENDERTARGET//第四个参数决定纹理的格式,不同的场景会要求不同的格式pd3dDevice->CreateTexture( TEX_WIDTH,TEX_HEIGHT,1,D3DUSAGE_RENDERTARGET,D3DFMT_R5G6B5,D3DPOOL_DEFAULT,&pRenderTexture,NULL);//获得pRenderTexture对应的SurfacepRenderTexture->GetSurfaceLevel(0,&pRenderSurface);

3.渲染场景

[html] view plaincopyprint?
  1. //这里保存下原来的Render target,在做完RTT后再恢复
  2. pd3dDevice->GetRenderTarget(0,&pBackBuffer);
  3. if( SUCCEEDED( pd3dDevice->BeginScene() ) )
  4. {
  5. //设置我们的纹理为render target
  6. pd3dDevice->SetRenderTarget(0, pRenderSurface);
  7. pd3dDevice->Clear( 0, NULL,
  8. D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
  9. D3DXCOLOR(0.0f,0.00f,0.00f,1.00f), 1.0f, 0);
  10. //重新将render target设置为帧缓存
  11. pd3dDevice->SetRenderTarget(0, pBackBuffer);
  12. pd3dDevice->EndScene();
  13. pBackBuffer->Release();
  14. }
 //这里保存下原来的Render target,在做完RTT后再恢复pd3dDevice->GetRenderTarget(0,&pBackBuffer);if( SUCCEEDED( pd3dDevice->BeginScene() ) ){//设置我们的纹理为render targetpd3dDevice->SetRenderTarget(0, pRenderSurface);pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DXCOLOR(0.0f,0.00f,0.00f,1.00f), 1.0f, 0);//重新将render target设置为帧缓存pd3dDevice->SetRenderTarget(0, pBackBuffer);pd3dDevice->EndScene();pBackBuffer->Release();}

4. 善后

[html] view plaincopyprint?
  1. SAFE_RELEASE(pRenderSurface);
  2. SAFE_RELEASE(pRenderTexture);
SAFE_RELEASE(pRenderSurface);
SAFE_RELEASE(pRenderTexture);  

这里需要注意的几点:

  • 渲染的时候要选择正确的纹理格式。如果你要在纹理里面保存高精度浮点数的话。通常所用的A8R8G8B8格式每一个颜色分量只有8位,只能表示0-255。详情可以参考DirectX SDK Help中关于D3DFORMAT的说明。
  • 如果你的纹理长宽比和帧缓存的不同的话,那么你需要在切换RenderTarget以后重新设置投影矩阵。否则渲染出来的图像会被拉伸。

  • 纹理的大小不能太大。经过试验发现是在窗口模式下面窗口和纹理的大小不能超过屏幕减去任务栏的大小。如果超过的话似乎对纹理的任何操作都不会有效果。(可能是深度缓存不够大,参考注意事项4)

  • 如果想要验证自己渲染出来的纹理是否正确,可以用D3DXSaveTextureToFile把纹理保存为图像。
  • 如果想要直接访问纹理中的值则要麻烦一些。按照SDK文档里面的说法,作为RenderTarget的纹理是保存在显存上面的,无法lock与unlock。要向访问其中的值需要做如下操作:
[html] view plaincopyprint?
  1. LPDIRECT3DTEXTURE9 text;
  2. LPDIRECT3DSURFACE9 surf;
  3. D3DLOCKED_RECT lockbits;
  4. pd3dDevice->CreateTexture(TEX_WIDTH,TEX_HEIGHT,1,0,
  5. D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM,
  6. &text, NULL);
  7. text->GetSurfaceLevel(0,&surf);
  8. if (pd3dDevice->GetRenderTargetData(pRenderSurface, surf) == D3D_OK)
  9. if (surf->LockRect(&lockbits, NULL, D3DLOCK_READONLY) == D3D_OK)
  10. {
  11. pRenderSurface->UnlockRect();
  12. float* bits=(float*)(lockbits.pBits);
  13. // SAVE BITS TO TEXT FILE
  14. FILE* ofile = fopen("output.txt", "w");
  15. for (int i=0; i<64; i++)
  16. {
  17. for (int j=0; j<64; j++)
  18. fprintf(ofile, "(%2.2f,%2.2f,%2.2f) ", bits[i*64*4+j*4], bits[i*64*4+j*4+1], bits[i*64*4+j*4+2]);
  19. fprintf(ofile, "\n");
  20. }
  21. fclose(ofile);
  22. }
  23. text->Release();
  24. surf->Release();
LPDIRECT3DTEXTURE9 text;
LPDIRECT3DSURFACE9 surf;
D3DLOCKED_RECT lockbits;pd3dDevice->CreateTexture(TEX_WIDTH,TEX_HEIGHT,1,0,D3DFMT_R5G6B5, D3DPOOL_SYSTEMMEM, &text, NULL);text->GetSurfaceLevel(0,&surf);if (pd3dDevice->GetRenderTargetData(pRenderSurface, surf) == D3D_OK)if (surf->LockRect(&lockbits, NULL, D3DLOCK_READONLY) == D3D_OK){pRenderSurface->UnlockRect();float* bits=(float*)(lockbits.pBits);// SAVE BITS TO TEXT FILEFILE* ofile = fopen("output.txt", "w");for (int i=0; i<64; i++){for (int j=0; j<64; j++)   fprintf(ofile, "(%2.2f,%2.2f,%2.2f) ", bits[i*64*4+j*4], bits[i*64*4+j*4+1], bits[i*64*4+j*4+2]);fprintf(ofile, "\n");}fclose(ofile);}text->Release();surf->Release(); 

这个技术可以用来在多通道渲染中传递渲染结果。比如可以把RTT出来的结果用来作为第二编渲染中的纹理来使用,这样可以实现水面反射等效果。另外在通用计算中可以用来保存数据。例如可以把GPU数值计算以后的结果保存在纹理中,再用上面所说的方法把数字读出来(如果真要这么做的话别忘了把纹理格式设置为足够大精度的格式,比如说A32B32G32R32F)。

转载至:http://shiba.hpe.sh.cn/jiaoyanzu/WULI/showArticle.aspx?articleId=451&classId=4

渲染目标是一个缓冲,显卡通过这个缓冲使用一个Effect类绘制场景的像素。

默认的渲染目标叫做后备缓冲- 物理上就是包含下一帧要绘制的信息的一块显存。你可以使用RenderTarget2D类创建另一个渲染目标,在显存中保留一块新区域用于绘制。大多数游戏在后备缓冲之外将大量的内容绘制到其他渲染目标内("offscreen"),然后编译这些不同的图像元素,将它们组合起来构成最终的后备缓冲。

一个渲染目标具有高和宽。后备缓冲的高和宽就是游戏的最终分辨率(而在Xbox 360中最终结果要进行缩放匹配用户的屏幕)。而一个offscreen渲染目标无需和后背缓冲有相同大小的高和宽,最终图像的小部分可以绘制到一个小渲染目标中,然后将它复制到另一个渲染目标。渲染目标还有一个surface格式,表明每个像素分配到多少bits和它们如何分割成红,绿,蓝,alpha通道。例如,SurfaceFormat.Bgr32给每个像素分配32 bits:每个颜色通道8 bits ,alpha通道8 bits。渲染目标还可以对所有绘制在其中的图像施加反锯齿(antialiasing)。

要使用渲染目标,需要创建一个指定高、宽或其他选项的RenderTarget2D对象。然后调用GraphicsDevice.SetRenderTarget将这个渲染目标作为当前渲染目标。从这一步开始,任何对Draw的调用会绘制到这个渲染目标。当结束渲染目标后,调用 GraphicsDevice.SetRenderTarget切换到一个新的渲染目标(或设置为null切换到后备缓冲)。 然后你就可以在任何时候调用RenderTarget2D.GetTexture获取渲染目标的内容进行后继处理。

;渲染目标可以和depth-stencil缓冲结合起来使用。如果你设置一个新的渲染目标,这个渲染目标会使用一个已存在的depth-stencil缓冲。如果新渲染目标有一个不同于depth-stencil缓冲的multisampling设置,或更大的宽和高,你就需要一个新的depth-stencil缓冲匹配这种情况。你还需要在depth-stencil缓冲中使用一个匹配渲染目标表面格式的深度格式。有时你可以同时渲染超过一个以上的渲染目标。你的图形设备支持的渲染目标的数量可以从MaxSimultaneousRenderTargets属性获得。使用多个渲染目标有很多变量,更多的信息可见Render Targets。

关于RenderTarget的注意事项

1. 设置一个RenderTarget会导致viewport变成跟RenderTarget一样大

2. 反锯齿类型必须跟DepthStencilBuffer一样

3. RenderTarget的类型必须跟DepthStencilBuffer的类型兼容, 可以用IDirect3D9::CheckDepthStencilMatch进行检测

4. DepthStencilBuffer的大小必须>=RenderTarget的大小

5. IDirect3DDevice9::SetRenderTarget的第0个不能为NULL

6. Usage为D3DUSAGE_RENDERTARGET的Texture不能进行反锯齿, 而且Pool必须为D3DPOOL_DEFAULT. 如果想利用RenderTarget做为纹理又想反锯齿, 可以先把场景渲染到一个CreateRenderTarget创建的Surface(或BackBuffer)上, 再用IDirect3DDevice9::StretchRect拷贝到纹理上

7. D3DX提供了一个ID3DXRenderToSurface, 简化了RenderTarget的使用. 注意它的BeginScene跟EndScene与再用IDirect3DDevice9的同名函数对不能嵌套, 因为实际上内部还是调用的IDirect3DDevice9的, 用PIX可以看到它进行了哪些调用. 还有就是这个接口仍然不能反锯齿, 而且每次都要保存/恢复一堆状态, 总觉得不爽

8. RTT不能既做为输入就做为输出目标, 某些显卡上可能只会给一个warning, 有些显卡上则会发生报错/黑屏/死机之类不可预计的事情...另外, Depth stencil texture(参见Hareware shadow map)也有同样的问题, 用完之后要SetTexture(n, NULL)清空, 不然A卡会黑屏/花屏/深度错误, 既使你没有使用, 只要它被寄存器引用了, 显卡还是会当做是正在使用的, 这时就不能做为depth stencil buffer

9. RTT如果想保存到文件中, 是不能直接SaveToTexture的. 需要创建一个OffscreenSurface, 拷贝过去, 再保存. 不过N卡好像不支持DXT1格式的OffscreenSurface, 可以创建Texture, 取其level0的surface代替.

10. N卡在开启了锯齿后冒似所有的RTT都要反锯齿, 不然深度测试会失败-_-

11. Intel的显卡在RTT没有设置DepthBuffer时可能所有绘制全部深度测试失败, 需要关闭深度测试再画.

12. SRGBWRITE不支持Float格式的RT

13. MRT时使用第一个RT的alpha来做alpha test

14. MRT不支持反锯齿, 必须相同bitdepth, 可以不同格式, 必须相同大小

什么是渲染目标(render target) 渲染到纹理(Render To Texture, RTT)详解相关推荐

  1. 【三维目标检测】VoxelNet(一):crop.py详解

    接下来将详细介绍VoxelNet三维点云目标检测模型,将参考Github上的源码进行介绍,Github地址为https://github.com/skyhehe123/VoxelNet-pytorch ...

  2. Target DVS 的 EDI SaaS解决方案—— LIP 操作详解

    本文将详细讲解针对 Target DVS EDI 需求的知行之云 LIP 系统操作. Target DVS 的 EDI 需求 传输协议:AS2 报文标准:X12 业务单据: DVS 业务报文代码 DV ...

  3. 深度学习目标检测2013-2018单双阶段主流模型概览及详解

    背景:深度学习引入目标检测领域以来,给目标检测领域带来了很多突破性的进展,文章"Deep Learning for Generic Object Detection: A Survey&qu ...

  4. 单阶段目标检测模型YoLo系列(一):YoLoV3详解及代码实现

    目录 1.YoLoV3网络结构 1.1 Backbone:Darknet-53 1.2 构建特征金字塔 1.3 YoLo Head 2.yolov3模型预测结果的解码 2.1 先验框 2.2 检测框解 ...

  5. STAPLE目标跟踪算法 基于C++ /Opencv实现步骤以及代码详解

    Staple Algorithm C++ implementation of staple algorithm for object tracking. 由于最近在做DSST和STAPLE算法跟踪效果 ...

  6. 【什么是渲染目标(render target)】

    渲染目标是一个缓冲,显卡通过这个缓冲使用一个Effect类绘制场景的像素. 默认的渲染目标叫做后备缓冲- 物理上就是包含下一帧要绘制的信息的一块显存.你可以使用RenderTarget2D类创建另一个 ...

  7. zbrush变化目标(Morph Target)子调控板解释

    使用zbrush建模时经常需要用到选择区域和目标的指令,今天小编就来和大家详解下zbrush变化目标(Morph Target)子调控板. Morpg(变化) 只有当变化目标已经保存了才激活,可以使用 ...

  8. 怎样用zbrush变化目标(Morph Target)子调控板

    使用zbrush建模时经常需要用到选择区域和目标的指令,今天小编就来和大家详解下zbrush变化目标(Morph Target)子调控板. Morpg(变化) 只有当变化目标已经保存了才激活,可以使用 ...

  9. 3d网上渲染平台是怎么渲图的_云渲染流程详解!

    题主说的看到许多网友对''3d网上渲染平台是怎么渲图的''进行提问,瑞云渲染小编也提供自己的小小见解.针对3D网上渲染平台是指什么,实际应该是指云渲染农场.几十年来,随着计算机软硬件不断更迭,图形图像 ...

最新文章

  1. HDOJ2569 ( 彼岸 ) 【递推公式】
  2. 小鱼天气android,小鱼天气(cn.microsoft.cig.uair) - 1.3.14 - 应用 - 酷安
  3. 音视频、AI和5G等技术在东京奥运会中的应用
  4. linux shmget shmctl
  5. 在家吃饭保平安,华人学者研究发现,经常在外就餐增加死亡风险
  6. vue中两种路由跳转拼接参数
  7. 关于DG32f103C8T6 不启动的问题-调试可以运行自启动不行
  8. java三大框架增删改查_Java_Web三大框架之Hibernate增删改查
  9. 不了解沙特,那你就看不懂硅谷
  10. PyQt5学习--基本窗口控件--QButton类控件
  11. python中的文字怎么居中_各位大神,wxPython中,怎么让text文本居中显示?
  12. HTTP常见状态码及常见错误
  13. java100内奇数和偶数的和
  14. ins无法发帖_Instagram电脑端直接消息怎么发?
  15. Faulty Robot-(dfs或者dijkstra)
  16. 《西方哲学史》阅读笔记
  17. 机器学习笔记之马尔可夫链蒙特卡洛方法(三)MH采样算法
  18. 为什么现在那么多公司都要招聘测试开发?测试开发是什么?
  19. 英语六级作文模板,写作小白救星
  20. 计算机应用基础ascii码运算,自考计算机应用基础真题及答案

热门文章

  1. mysql中的split函数_mysql中的split函数
  2. split()函数的用法
  3. STM32CubeMX之定时器PWM输出
  4. Objective-C错误码
  5. scaleform 注意事项
  6. golang反向代理使用
  7. 360和QQ之战——之我见
  8. ionic android 闪退,ionic 程序部署到ios设备闪退或白屏的解法方案
  9. librosa安装技巧
  10. 全国唯一!烽火助力江夏数字乡村试点改革工作入选中国年度深化改革典型案例...