如果你是个赛车游戏的爱好者,你一定玩过风靡全世界的赛车游戏《极品飞车:地下狂飚2》 (Need For Speed : UnderGround2)。在这款游戏中,玩家扮演了一个靠赛车为生的地下飚车手,在永远是夜晚的城市中飞驰。这款游戏大量地运用了环境映射技术(Real-time Evironmnet Mapping),例如下过雨的公路上看到两旁夜景的倒映、玩家车身上对周围环境的真实反射等等,这些都是环境映射技术的功劳。这种最酷、最华丽的特效现在已经被应用在大量实时虚拟显示游戏中,将来它也许会成为游戏中必不可少的元素。

  环境映射贴图技术最典型的应用就是车体的“流光”,这种现象在现实中非常普遍。当你开着车行驶在满是霓虹灯的街道上时,周围的灯光会在车身上投射出一个光斑或光带(前提是车子洗得非常干净)。当车子在街道上飞驰的时候,就好像一个个五彩的斑点在你的车体上流动,产生非常炫目的效果。甚至当你和别的车子擦肩而过时,别的车子也会映射在你的车体上。

要实现环境贴图映射首先需要一组环境信息。假设在场景里有一辆车(本文中将一直使用这个的例子),车的环境信息,即除车以外,你在车的位置上向四周看到的全部画面。你用相机对六个不同方向拍出六张照片,这样就产生了一组环境信息。车上的每个象素都和这个环境信息中的一个点有对应关系(也就是所谓的映射)。在最后渲染车时,需要把车身上每个环境对应点与车体本身的效果做混合处理。

  归纳起来要实现环境贴图映射需要执行以下步骤:

  A.创建环境贴图

  B.把场景中的物体渲染到环境贴图上

  C.渲染车体时,把原始贴图和环境贴图经过处理后最终渲染到屏幕上

  准备好你的车子了吗?你一定想马上体验一下给自己车子打上环境贴图是什么感觉吧!不过在动手之前,你还需要对相关知识有所了解,否则是很难找到正确方法的。

  一、什么是环境贴图?

  在上面我用照相机作过比喻,在这个比喻中,那六张照片就类似一组环境贴图,它们代表了立方体的六个面。你可以想象一下,当你的眼睛处于这个立方体时,你会看到周围所有的景物,因此我们将它们称为环境贴图。

  一般来说我们所使用的环境贴图都是用立方体作参照,因此通常称为立方体贴图(Cube Map)。每个Cube Map包含六个面,在DirectX中分别用+X、-X、+Y、-Y、+Z、-Z来表示,其每个面都在水平和垂直平面上都覆盖了90度的视角。

  二、我们要如何操作这组Cube Map呢?

  在DirectX 9中,分别使用了六个不同的标识来表示Cube Map中的各个面。这组标识叫D3DCUBEMAP_FACES,其中的D3DCUBEMAP_FACE_POSITIVE_X就代表了Cube Map中以中心点为原点的正X轴所指向的那个面,依此类推。而Cube Map的每一个面都是一个LPDIRECT3DSURFACE9对象,当你需要对其中某个面进行某些操作时,就需要先调用GetCubeMapSurface函数来得到你所指定的那个面(Surface),然后像操作一般面一样操作即可。而本文中将要对其进行的操作就是把面作为渲染目标(Render Target),把周围环境中的景物全部绘制到这个Cube Map上。因为Cube Map在每个方向上有一个面,每次绘制其中一个面,就要对场景中所有的景物进行一次渲染。这个过程的开销是十分大的。如果你的场景中某个物体上使用了环境映射,那么原来只需绘制一次的场景,就会增加6倍的渲染消耗!如果一个游戏原来跑70帧,当绘制一个使用了环境映射的特效时,马上降到了10帧!这是一个相当可怕的数字!你也许会考虑:那么这项技术会不会因为开销太大而失去使用的价值?答案是不会的。以目前人类的智慧已经可以非常好的处理好环境映射的效率问题,所以不必担心,而且在后文中我们还会详细讲解这些方法。

  三、如何使用Cube Map?

  当你渲染好了贴图坐标,下一步就是如何使用的问题了。要想将一张贴图绘制到屏幕上,就需要用坐标和模型的顶点进行对应。我们都知道,2D贴图使用u、v两个坐标来表示顶点对应的贴图坐标。而在Cube Map中,仅有2个量是无法表示一个点在立方体中的位置的,所以,Cube Map的贴图坐标是由3个数的向量来表示的,你可以简单把这个贴图坐标对应的颜色理解为:从盒子中心向这个3D向量方向前进直至与盒子相交,其交点的象素颜色值。比如你要让场景中的一个球映射出周围的环境,最简单的做法是把球上每个点的法线做为贴图坐标传给图形处理芯片,这样一来就能让球具有金属质感。不过,仅仅这样简单对应的话,绘制出的图将会和现实世界的映射有所出入。

四、环境映射贴图实际如何操作?

  为了应用程序开发方便,微软DirectX 9提供了一个ID3DXRenderToEnvMap的接口,通过这个帮助类接口,我们可以十分快速地完成对环境贴图的操作。当然,作为一个通用的接口,它不仅含有对Cube Map的操作,还支持其他一些环境贴图,比如Sphere Map等。我们可以通过查看DirectX 9的相关文档了解更多信息。

以下代码实现的即是创建环境贴图的工作:

  #define CUBEMAP_SIZE 128

  IDirect3DCubeTexture9* m_pCubeMap;

  ID3DXRenderToEnvMap m_pRenderToEnvMap;

  // 创建RenderToEnvMap对象。其中m_pd3dDevice和m_d3dsdBackBuffer分别是指向Direct3D设备和显示缓存页面的指针。CUBEMAP_SIZE这个宏指定了Cube Map的边长,它关系到环境贴图的大小,值越大占用空间越多,绘制也越慢。

  if( FAILED( D3DXCreateRenderToEnvMap( m_pd3dDevice, CUBEMAP_SIZE, 1,

  m_d3dsdBackBuffer.Format, TRUE, D3DFMT_D16, &m_pRenderToEnvMap ) ) )

  {

  return E_FAIL;

  }

  // 创建空的Cube Map,D3DUSAGE_RENDERTARGET说明创建的Cube Map是一个渲染目标。

  if( m_d3dCaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP )

  {

  if( FAILED( D3DXCreateCubeTexture( m_pd3dDevice, CUBEMAP_SIZE, 1,

  D3DUSAGE_RENDERTARGET, m_d3dsdBackBuffer.Format, D3DPOOL_DEFAULT, &m_pCubeMap ) ) )

  {

  return E_FAIL;

  }

  }

  else

  {

  return E_FAIL;

  }

  

  绘制过程需要先得到一个新的投影矩阵(Projection Matrix),这个矩阵设置了摄像机的视角和场景深度范围等。另外还要计算出一个新的观察矩阵(View Matrix),这样Cube Map才会在“车”上随着视角的改变而变化。

HRESULT RenderSceneIntoEnvMap()

  {

  HRESULT hr;

  // 设置投影矩阵

  D3DXMATRIXA16 matProj;

  D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI * 0.5f, 1.0f, 0.5f, 1000.0f );

  // 得到当前观察矩阵

  D3DXMATRIXA16 matViewDir( m_matView );

  matViewDir._41 = 0.0f; matViewDir._42 = 0.0f; matViewDir._43 = 0.0f;

  // 把场景绘制到Cube Map上

  if( m_pCubeMap )

  hr = m_pRenderToEnvMap->BeginCube( m_pCubeMap );

  if(FAILED(hr))

  return hr;

  for( UINT i = 0; i < 6; i++ )

  {

  // 设置Cube Map中的一个面为当前的渲染目标

  m_pRenderToEnvMap->Face( (D3DCUBEMAP_FACES) i, 0 );

  // 计算新的观察矩阵

  D3DXMATRIXA16 matView;

  matView = D3DUtil_GetCubeMapViewMatrix( (D3DCUBEMAP_FACES) i );

  D3DXMatrixMultiply( &matView, &matViewDir, &matView );

  // 设置投影和观察矩阵并渲染场景(省略)。注意:这里的渲染场景中的物体并不包括使用环境贴图的物体。

  ... ...

  }

  m_pRenderToEnvMap->End( 0 );

  return S_OK;

  }

  渲染出环境贴图后,需要把这个环境贴图运用在目标物体上,即将其作用在我们的车子上。我们在渲染场景(真正绘制到屏幕的时候)中使用一个特效(Effect)来绘制。特效可以从内存中一段字串创建,也可以从一个特效文件(后缀名为fx,由DirectX9 SDK自带的Effect Edit生成)来创建。特效的使用方法可以查看SDK中的相关文档和例子,因为涉及的东西非常多,所以不在这里多说了。特效文件的代码如下,这里给出了基于固定渲染管道(Fixed Function Pipeline)的实现代码,除此之外还可以使用可编程渲染管道(Programmable Pipeline)来实现。

  textureCUBE texCubeMap;

  matrix matWorld;

  matrix matView;

  matrix matProject;

  technique Cube

  {

  pass P0

  {

  // Vertex state

  VertexShader = null;

  WorldTransform[0] = <matworld></matworld>;

  ViewTransform = <matview></matview>;

  ProjectionTransform = <matproject></matproject>;

  // Pixel state

  Texture[0] = <texcubemap></texcubemap>;

  MinFilter[0] = Linear;

  MagFilter[0] = Linear;

  AddressU[0] = Clamp;

  AddressV[0] = Clamp;

  AddressW[0] = Clamp;

  ColorOp[0] = SelectArg1;

  ColorArg1[0] = Texture;

  TexCoordIndex[0] = CameraSpaceReflectionVector;

  TextureTransformFlags[0] = Count3;

  }

  }

  需要注意的几点:

  1.用静态环境贴图代替实时渲染

  除了实时的渲染环境贴图外,另一个比较常见的做法是从文件中读取一张预先生成的环境贴图。静态贴图实现起来比实时渲染简单,它不需要把场景绘制到Cube Map上,只需在创建时从硬盘文件中载入即可。静态贴图可以大大降低渲染开销,是一种非常适合低端环境的技术。不过静态贴图会大大降低游戏的真实性,如果采用了静态贴图,那么车体上的贴图是不会随着环境改变而变化的。

  2.减少实时渲染的渲染开销

  除了使用静态环境贴图外,还可以使用其他方式来降低系统开销。比如《地下狂飚2》使用了限制环境贴图刷新率的办法来实现实时渲染,就是说规定每秒环境贴图只渲染一定的次数,而不是每帧都做渲染。而且这个频率可以让玩家来调整。另外一个方法是限制场景中渲染的物体数量。比如车子开到路段A上,就只会对路段A两边的建筑进行绘制;或者可以只绘制一部分能够被清晰映射的物体。除此之外还可以减小环境贴图的大小,但是这会对渲染效果产生影响,车体上的环境映射效果会比较模糊。

  3.使用可编程渲染管道

  使用可编程渲染管道大大提高了显卡编程的灵活性,随着显卡性能的逐日提高,可以由显卡绘制出更真实的特效。对于环境贴图来说,上面的代码是使用顶点计算贴图坐标,如果使用了逐象素(Per-Pixel)计算,将会把效果表现的更加真实,当然这样做的开销太大,目前仅限于顶级显卡。

实时环境映射贴图(Real-time Evironmnet Mapping)相关推荐

  1. 通向天才之路 : 实时环境映射贴图技术(Real-time Evironmnet Mapping)

    通向天才之路 : 实时环境映射贴图技术(Real-time Evironmnet Mapping) <Made In Coollen> 我小心翼翼地剪开了包着橡胶质外皮的线,祈祷着上帝再给 ...

  2. Asp.net mvc 实时生成缩率图到硬盘

    之前对于缩率图的处理是在图片上传到服务器之后,同步生成两张不同尺寸的缩率供前端调用,刚开始还能满足需求,慢慢的随着前端展示的多样化,缩率图已不能前端展示的需求,所以考虑做一个实时生成图片缩率图服务. ...

  3. echarts折线图怎么从y轴开始_基于echarts的双y轴实时更新折线图

    一款基于echarts的双y轴实时更新折线图效果,页面加载后开始自动更新数据并绘制对应的折线图,可以点击右上角的按钮:显示数据视图.刷新数据和将数据存储为png的图片. 查看演示 下载资源: 46 次 ...

  4. <Linux开发> ubuntu开发工具-Ubuntu测试网速及实时网速图

    <Linux开发> ubuntu开发工具-Ubuntu测试网速及实时网速图 一.查看网线上行.下行网速 1.安装speedtest-cli工具 water@water-Tower-PC:~ ...

  5. 伦敦金天天实时行情走势图

    伦敦金天天的走势图走势图中都有交易的机会,但高质量的交易信号和进场时机不是经常出现,如果能够过滤掉不佳的交易信号,大家的投资绩效就有望大幅提升.在每天的实时行情走势图中,长影线K线是高胜率的信号,它代 ...

  6. Python基于周立功盒子接收特定报文信号并实时绘制折线图(二)

    Python基于周立功盒子接收特定报文信号并实时绘制折线图(二) 一.背景     根据在上一篇文件Python基于周立功盒子接收特定报文信号并实时绘制折线图(一)的基础上需要做一些优化,原因是,因为 ...

  7. Python基于周立功盒子接收特定报文信号并实时绘制折线图(一)

    Python基于周立功盒子接收特定报文信号并实时绘制折线图(一) 一.背景     为了节省成本,最大限度利用资源,放弃了用Vector的盒子,采用周立功盒子来做二次开发,以方便来进行压力测试 二.需 ...

  8. 伦敦金实时行情走势图的魅力

    一直以来,伦敦金都因其自由的交易时间和不设涨跌幅限制而充满魅力,它每天的实时行情走势图是全球投资者关注的焦点,任何投资者都可以结合技术和基本面分析手段,来预测未来金价走势,从而在这个市场上实现收益. ...

  9. 客房管理实时房态图布局

    开发工具与关键技术:VS   后端 作者:陈芝番 撰写时间:2019.5.27 1.首先运用无序列表清单<ul></ul>列表标签,定义一个标有圆点的列表(无序列表) < ...

  10. CodeMapping:稀疏SLAM实时密集建图(帝国理工学院)

    作者丨黄浴@知乎 来源丨https://zhuanlan.zhihu.com/p/410825166 编辑丨3D视觉工坊 在2021.6接收于IEEE Robotics And Automation ...

最新文章

  1. python模块学习(四)
  2. CactiEZ V10.1 中文版 Cacti中文解决方案+使用教程(1)
  3. [YTU]_2906( 多重继承 日期与时间)
  4. 福建2020年3月计算机二级报名时间,福建2020年3月计算机二级考试报名时间安排...
  5. 在阿里云 ECS 上试图安装 SAP Commerce Cloud 的 137 错误
  6. $.post请求的参数在后台代码中得到为null_vue中Axios的封装和API接口的管理
  7. python 多线程同步_Python利用多线程同步锁实现多窗口订票系统(推荐)
  8. 小小智慧树机器人_中国工厂番外篇丨AGV机器人演绎智能“搬运工”
  9. 学习OpenStack之(6):Neutron 深入学习之 OVS + GRE 之 Compute node 篇
  10. Redis实战(11)高级特性(3)持久化
  11. 苹果Mac定制化App开发神器:​​​​FileMaker
  12. Java练习题--员工类案例练习
  13. Linux 配置虚拟IP
  14. QQ通过xml卡片自动探测对方ip
  15. 烽火通信 c语言 笔试,C语言嵌入式笔试题目及参考答案-烽火通信.doc
  16. 用Python模拟同步时钟
  17. 爱奇艺开源FASPell项目
  18. 智能识别系统设计---图像特征提取
  19. IPQ5018测试问题之Connect to QPST server
  20. 共阴极数码管,学号显示实验

热门文章

  1. 【5G NR】ZP CSI-RS资源配置
  2. 为什么建议你开一个电子签名钥匙盘?电信设备进网许可证办理
  3. Robocode简介
  4. cad添加自己线性_如何在CAD中添加自行创建的线型
  5. SpringMVC 刷课笔记
  6. 【机器学习】如何使用随机网格搜索,以缩短网格搜索速度?
  7. 《大数据之路》阅读笔记--数据同步
  8. Java设计文本编辑器
  9. 少儿计算机基础知识,儿童计算机基本操作
  10. Java项目:springboot网上书城系统