公告牌技术



公告牌技术,即billboard技术,在3D游戏中有着广泛的应用.它的本质就是用预先做好的几幅
位图来代替3D物体,极大地节省资源和提高速度.仔细观察<<魔法门>>系列游戏,它的精灵,树木,物
品都是二维图象,但由于它始终朝向观察者,你根本看不到它"扁"的一面,所以给人一种立体的感觉.
这种技术最大的优点是快.试想一个多边形构成的3D精灵,至少百余个多边形,而用billboard技术,
只需处理两个多边形,优势不言而喻.其他3D游戏的爆炸效果,<<极品飞车>>中路旁的树木,都使用了
该技术.

实现该技术的两个关键的步骤:
1.在合适的地方,朝合适的方向放位置一面"公告牌".这个公告牌,其实是一个共面四边形,一般使用
矩形.
2.把位图贴到矩形上.       
1.方向的计算:
如果是做爆炸效果,要求该矩形的法向量始终指向观察者的眼睛,设矩形的初始法向量与Z轴重合,可
以用以下代码获得其旋转矩阵:
D3DVECTOR vEyeLoc; //观察者眼睛的位置
D3DVECTOR vLoc; //公告牌位置
D3DVECTOR vAxis; //转轴
float fRads; //旋转角
D3DMATRIX matRotate; //旋转矩阵 
//赋值语句略去......
D3DVECTOR v=vEyeLoc-vLoc;//从放置点指向视点的矢量
vAxis=CrossProduct(Normalize(v),D3DVECTOR(0,1,0));//用叉积求转轴
fRads=asin(Magnitude(vAxis));//该叉积的模的反正玄正好是旋转角
D3DUtil_SetRotationMatrix(matRotate,vAxis,fRads); //设置旋转矩阵 
如果是做精灵,树木等,要求该矩形的法向量始终指向观察者且与地面平行,则更简单:
D3DVECTOR v=vEyeLoc-vLoc;//从放置点指向视点的矢量
vAxis=CrossProduct(Normalize(v),D3DVECTOR(0,1,0));//用叉积求转轴
D3DUtil_SetRotationMatrix(matRotate;,vAxis,g_PI/2.0); //g_PI=3.1415926
2.贴图的方式:
贴图的方式有两种,一是用颜色键,一是用Alpha.前者适合做精灵和树木等,后者适合做爆炸效果.
a)对于颜色键方法,如果是调色板表面,需要设置调色板;如果是高彩或真彩表面,需要逐点设置
Alpha信息.具体可参照d3dtextr.cpp里的CopyBitmapToSurface函数.其实也可以简单地使用
下面的代码(必须include d3dtextr.h):
//创建纹理,白色为透明色
D3DTextr_CreateTexture( "texture.bmp", 0, D3DTEXTR_TRANSPARENTWHITE);
......
//打开Alpha
pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE, TRUE );
//设置纹理层参数
pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
//设置混合因子
pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE);
pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_ZERO);
//绘图元
...........
b)直接使用Alpha.如果做的是爆炸效果,当然爆炸的边缘是半透明的,可以如下设置:
//假设你的爆炸的位图以黑色为背景
//打开Alpha
pd3dDevice->SetRenderState( D3DRENDERSTATE_ALPHABLENDENABLE,TRUE );
//设置混合因子
pd3dDevice->SetRenderState( D3DRENDERSTATE_SRCBLEND, D3DBLEND_ONE);
pd3dDevice->SetRenderState(D3DRENDERSTATE_DESTBLEND,D3DBLEND_ONE);
//绘图元
...........
(完)


6.4.3 公告牌技术

《OPhone/Android游戏开发与推广指南》第6章 OPhone 3D游戏开发,本章将通过剖析一个完整的3D游戏开发案例,向读者介绍有关OPhone平台中3D游戏开发所用到的相关技术,包括通用程序框架、3D渲染流程、纹理对象操作、天空盒、公告牌、模型渲染、骨骼动画、2D菜单渲染、相机系统、碰撞检测、粒子系统以及有限状态机等内容。本节为大家介绍公告牌技术。

AD:51CTO 网+ 第十二期沙龙:大话数据之美_如何用数据驱动用户体验

6.4.3  公告牌技术

在场景中,除了地面,我们还增加了一些树木、小草作为点缀。对于这些树木、小草的渲染,如果采用实体模型,每个叶子都要用三角形来建模表示,这对于移动设备来说显然负载太大。因此常用的做法是使用公告牌(BillBoard)技术进行替代渲染。

公告牌实际上就是采用一种时刻面对相机的四边形面片来代替复杂模型的渲染方式。公告牌是一个2D空间中的面片,没有厚度的概念,只有正反面。因此要想不穿帮,必须时刻将四边形的正面对准相机。在生成公告牌贴图时,可以制作具有一定3D透视效果的2D贴图,这样贴图应用到公告牌面片上时,会给观察者一种3D透视的感觉。比如在《极品飞车》早期版本中,就大量采用了公告牌来渲染道路两边的树木。

公告牌技术的核心,就是如何与相机对齐,以确保观察者始终看到其正面。公告牌的对齐方式有很多种,根据不同需求,可以有轴对齐、屏幕对齐或者面向世界对齐等。在本游戏中,我们使用公告牌来渲染树木、小草等地面附属物,因此它们的Y向量始终垂直于地面。所以,我们需要使用Y轴对齐方式,实时根据相机的朝向来调整公告牌的相应朝向。

除调整公告牌朝向之外,还需要指定每个公告牌的位置、尺寸以及纹理贴图对象等。渲染时,首先根据当前相机计算出公告牌对象的朝向矩阵,再根据指定的位置、尺寸等信息计算出最终矩阵,然后将面片Quad类变换到指定位置处结合指定纹理加以渲染。计算最终矩阵与公告牌最终渲染的相关代码如下:

   
   
  1. /**  
  2.  * 计算公告牌最终矩阵  
  3.  * @param cam  
  4.  */  
  5. private void composeTransform(ICamera cam) {  
  6.     gMatTransform.setIdentity();  
  7.     //根据相机朝向调整公告牌朝向  
  8.     gMatTransform.SetRight(cam.getCamRight());  
  9.     //Y向量始终垂直于地面  
  10.     gMatTransform.SetUp(Vector3f.UNIT_Y);  
  11.     gMatTransform.SetForward(cam.getCamDirection());  
  12.     //计算公告牌尺寸矩阵  
  13.     gMatScale.setIdentity();  
  14.     gMatScale.setScaleMatrix(mfWidth, mfHeight, 1.0f);  
  15.     //合并矩阵  
  16.     gMatTransform.mul(gMatTransform, gMatScale);  
  17.     //将公告牌矩阵位置设置为指定位置  
  18.     gMatTransform.setTranslation(mvPosition);  
  19. }  
  20.  
  21. /**  
  22.  * 渲染公告牌  
  23.  * @param gl  
  24.  * @param cam  
  25.  */  
  26. public void render(GL10 gl, ICamera cam) {  
  27.     //首先计算公告牌最终矩阵  
  28.     composeTransform(cam);  
  29.     //开始渲染公告牌面片  
  30.     gl.glPushMatrix();  
  31.     {  
  32.         //由于贴图有透明信息,因此启用混合操作  
  33.         GLFactory.enableBlend(gl);  
  34.         //应用公告牌最终矩阵变换  
  35.         gl.glMultMatrixf(gMatTransform.AsFloatBuffer());  
  36.         //传入指定纹理,渲染面片  
  37.         Quad.GetInstance().drawTexturedQuad(gl, mTexInfo.mnTexID);  
  38.         //渲染完毕,禁用混合  
  39.         GLFactory.disableBlend(gl);  
  40.     }  
  41.     gl.glPopMatrix();  
  42. }  

在游戏中,我们会在不同的位置随机生成若干棵树木和小草。渲染场景时,再对这所有的公告牌进行统一渲染。因此我们设计了一个公告牌管理类,在游戏初始化时随机生成公告牌对象,并统一更新渲染所有的公告牌对象。在渲染所有的公告牌时,由于贴图均带有透明度,渲染时会有前后遮挡的问题,所以需要预先按照对象与相机的距离进行排序,然后按照由远及近的顺序进行统一渲染。

   
   
  1. /**  
  2.  * 载入所有对象  
  3.  * @param cam  
  4.  */  
  5. public void load(ICamera cam) {  
  6.     clear();  
  7.     //随机添加树木  
  8.     randomAddTrees(cam);  
  9.     //随机添加小草点缀  
  10.     randomAddGrass(cam);  
  11.     //将ArrayList转换为数组,进行排序  
  12.     mpObjects = new BillBoard[mListObjects.size()];  
  13.     for(int i = 0; i < mpObjects.length; i++) {  
  14.         mpObjects[i] = mListObjects.get(i);  
  15.     }  
  16.     //对所有公告牌按照与相机的距离由远及近进行排序  
  17.     Arrays.sort(mpObjects);  
  18. }  
  19.  
  20. /**  
  21.  * 渲染已经排序好的公告牌  
  22.  * @param gl  
  23.  * @param cam  
  24.  */  
  25. public void renderAll(GL10 gl, ICamera cam) {  
  26.     //渲染时,由远及近进行渲染  
  27.     for(int i = 0; i < mpObjects.length; i++) {  
  28.         mpObjects[i].render(gl, cam);  
  29.     }  
  30. }  

在上面的代码中,创建了所有的公告牌之后,用一个数组保存所有的对象,然后对这个数组,按照公告牌与相机距离的远近,调用Arrays.sort()进行排序。这就需要使用BillBoard类实现Comparable接口,并重载compareTo()函数。相关代码如下:
   
   
  1. public class BillBoard implements Comparable<BillBoard>{  
  2. /**  
  3.  * 按照与相机的距离由远及近进行排序  
  4.  */  
  5. @Override  
  6. public int compareTo(BillBoard b) {  
  7.     if(this.mfDisToCamera < b.mfDisToCamera) {  
  8.         return 1;  
  9.     }  
  10.     return -1;  
  11. }  





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值