欢迎访问xmchang的博客
Android 文字绘制到Bitmap上
Andriod 使用SurfaceView的OpenGL ES基本框架(sdk1.5)
Andriod Java OpenGL ES基本框架(sdk1.5)
十一节期间,对G2引擎做修改
G2引擎是1年前开始写的,前面的一些Demo图象都是使用了G2.
之所以叫G2,因为以前花了很长很长时间学习golgotha引擎(令人难忘的引擎,后来我基于它制作了游戏<啊平型关>,不幸和golgotha引擎的命运一样被打入冷宫),一些思想都是从它继承的,然而golgotha太复杂了,用起来难度很大.但要自己写个"引擎"的确是个浩大的工程,一直不敢去尝试,终于有一天心情不错,开始写了几段,就一直写下去了.写得烦躁了,就扔下休息.心情好的时候继续.半年前初步成型了.叫G2意思是golgotha的第2版,又比golgotha精练和模块化分明.新的内容是对HLSL的封装,因为golgotha时代还没有这个东西.
构架比较理想,制作个演示变得很容易了.那些时髦的内容比较方便地制作个独立的object,加入系统中,因此,把bsp,terrain,mdl等,全加进去了.
但是,所有新的内容都需要用C来写,还是比较费劲,且没有使用COM接口,只使用了DLL接口,模块修改起来相互干扰比较厉害,也懒的学COM了.
十一期间,7天长假,用来做个结构性改变的大工程.(好羡慕人人都去旅游,想不明白中国怎么一下变成都是富人了)
结构性变化的用意是将Lua引入G2引擎,达到一个类似c++builder的境界,原来的object变为控件,可以通过一个属性表来设置属性,可加入事件函数,为Lua的程序.这样,制作内容的过程就变的比较简单了,不需要再去C编译.
于是,把G2核心进一步简化,把原来包含其中的object管理器,渲染器都独立出来作为driver,只是driver管理其留在中间,其实也可以扔出来,但把Driver管理器放到driver组里面去不合适,父亲和儿子不能一组.原来都使用Class成员指针记录各个Driver,如display_drv,model_drv,使用起来很方便,但有个问题,就是g2.dll一加内容,class结构变了,其他的driver及object需要重新编译才行.改为一个指针数组,将各driver变为序号,DRV[DISPLAY_DRV],加入一个get_drv(int n)取得driver地址,再加个宏定义,#define g2_display_drv g2->get_drv(DISPLAY_DRV),这样,原来的g2->display_drv,简单修改为g2_display_drv即可.好处是加driver时,class不变,这样g2的小变化,其他driver及object不必要重新编译.
引擎性能优越了一些,经过几天奋斗,终于让它又运转起来了.编写了一个测试的Lua驱动.
但对lua的引入仍未能实现,需要将object作个可控制的参数表,这样,原来的int,bool之类的C方式显然不行,必须有参数的名字,可以外部取得参数表,可定义数值,参数可能是多项选择性,如bool可能有true及false选择器,其他可能有多项选择.lua事件脚本也能读写参数表,这样,可控制这个object.
属性编辑器以前看过的nvidia的cgfx编辑器的,不幸硬盘找不到了,是包含在cgtool1.1里的,新版本把它扔了.从nvidia网站拉了几次也拉不下来,太大了,用讯雷下载nvidia的东西总是出现文件错误.
如何操控仍是个谜团,对图象的动画可以做个多个序列的图片,定义序列为"站立","行走","跑",直接播放指定序列及可.
对object要复杂了,比如,一辆Tank,需要不同方向开动,对地形反映,转动炮塔,开火,被击中,变为残骸.用C写比较固定的内容,比如炮塔的转动,履带的转动等.Tank的各个model可以参数变更,这样,更换model就成了不同Tank,没有炮塔就是装甲运兵车了.运行的速度可以定义,以便重型Tank和轻型Tank的不同.那Lua脚本做什么用?比如,我们在场景中一下扔10辆坦克,都定义为不活动的不可见的,只有一个Think过程在运转,这样,场景中似乎没有Tank这个东西,当条件具备时,比如到一定时间,Tank才按序出现,并各按其路线前进,出现障碍,选择绕行路线,发现敌人,开火,直到壮烈牺牲了,变为一个没有思想的障碍物.C写硬性过程,Lua写软性的东西.
接下去,再继续确定object参数表,用Lua控制它.结果如何,Lua接口如何自动生成,仍需要研究.以前有个CBP可自动封装Lua,但竟然不支持dll,麻烦.
目标只有一个,当做完后,自己感觉做的很棒.肯定不想OpenSource,但拿它当个下蛋的鸡也好难.路需要向前走,走向哪里,有首歌说得好"未来会怎样怎样?我无法猜想......"
上文如同天书,只有自己明白.以前有个笑话,说女人说话不加思考,女人回答,我不说出来如何思考? 同理,有时候自己有事不明白,写着写着就明白了许多.
20061009
旧文拾遗:Nebula2原理初探(续3)
半年不做研究和写文章了,总是为衣食奔波,硬盘上发现这篇1年多前写的小文章,前面几篇发在别人的论坛上,找不到了,只剩这篇尾巴,读起来莫名其妙,但也有点周星星的无喱头风格,贴出来,作为一个过度......
Nebula2原理初探(续3):超越
在前面几篇,我们勾画出Nebula2的确美好画面.但是,现实是复杂的,那些所说的美好的东西,哪些是真的,哪些不是真的?往往不是凭感觉。我们能够做什么?仍然是个问题。
1.层次模型的骗局
层次结构能加快搜索速度,似乎大家一听,都感觉有道理。比如,有1000个对象,顺序搜索的话,最多要1000次。等分成四组,那只需要250次,加组的判别,只需要多加四次。但是,仔细一想,随便哪个程序员,也不会傻到一个个去比较。可以采取二分法查找,那1000个对象,最多只要10次就找到了。如果等分成4组,需要8次,加上组的判别需要2次,哪仍然是10次,一点没快。如果分成三个层次,只会次数更多。
而为了这个并不见提高效率的层次,我们要付出很大的代价,要编写记录许多的层次……这样的话,我们有什么必要采用这样的层次模型?
2.更好而简单的办法:
我们可以采取一个简单的数组存储所有的对象名字,在Game开始事,将所有脚本先行载入。这时,我们查找一脚本里所有对象,把它转换为数组的序号,成为一个“编译”了的脚本,执行时,直接到数组里去取,不就不需要任何查询了吗?
这个“编译”过程,所花费的时间,相比Game载入几十兆的图象所花的时间,简直是不值的一提。这是一个解决方案。应该还有其他的方案。
3.N2E的新解决方案
我们在脚本载入前,必须加入一个预“编译”器,将其调用的对象提前变更为数组的序号。
当然,脚本现在已有不少现成的编译器,我们大可以研究一下,改进成我们需要的编译器。
我们最终的Game,应该不会是解释方式执行的,所以,我们必须在解释和编译之间,寻找一个结合点。
这样,我们可以构造一个全新的构架。
4.N2E改名
既然我们不认为Nebula2的骨架确有优越性,那我们就大可不必采取Nebula2的骨架。而采用我们自己的骨架。而我们原先命名的N2E(Nebula2Edit)系统,自然就就没有必要在用这个名字了。我们大可以采取自己的骨架,自己的名字……我们可以取个具有时代特色的名字,比如SDH1:意思就是“苏丹红一号”…….当然,这样毒性有点大,要起个可爱的名字,还需要再考虑。
当然,对Nebula2的牛肉,我们也没有必要一古脑丢掉,闲来无时嚼嚼看,也许有些营养。看见旁人推崇Nebuka2的速度,我不感苟同,我运行时,感觉很Slow,远远达不实用的程度。
5.C#是一条路吗?
C#是个新的解决方案,可以结合多种语言。它所兼容的Java,就是解释性语言。如果用它来打造一个编辑器,具有可行性。不过,因为它太庞大了太新了,还没有看到什么具体的应用,对它还是一知半解的,也许行,也许不行…….这需要懂的人来解答。
6.总结
混沌……上面所述的,使我们在还没有踏上征程时,已经对前途充满迷茫。
只是,我觉得我们仍然该做点什么。我们的程序员,在确知自己不如美国人、俄国人、印度人、巴西人及韩国人之后,是否要向德国人低头,仍然还需要考虑一下。
我们应该可以做点什么,这样,至少我们的将来,软件水品可以不至于落在其他的国家后面,例如尼加拉瓜,爪哇的后面……
呵呵,说这话,不要杀我呀。我们目前至少还有值得骄傲的金山软件,盛大软件替国人扬威呢。
(Nebula2原理的胡乱初探,到此告一段落)
笨笨 2005.4.17
ATI的Octree的terrain Demo简要分析
ATI总是被Nvidia的光辉遮挡,其实他们常有一些好东西,如Rendermonkey等.
这个以前偶然在他们网站上看到的terrain demo,就是个不错的东西.这个Demo在Nvidia卡上也能很好地运行.

初看以为是个和其他演示类似的东西,但地形起伏有点看起来有点异样,后面居然钻入长长的地洞,的确让人吃惊.
简单看了一下,原理也不复杂,就是不使用LOD技术,而改用Octree,从四叉树改为八叉树.这样,地形可以任意变化.
但是,这样又出现新问题了,对于使用LOD的,可以简单地使用Perlin等生成多变的高度图来得到Terrain地形,而OCTree的地形如何生成.就象这个Demo的内容是如何制作出来的?ATI没有给出解释.也许是费了牛劲抠出来的.
另外,LOD方式可以比较容易地根据距离做简化,以提高渲染速度,对Octree的内容如何简化?仍然是一件困难的事情.
因此,这个Demo看过后,觉得没有什么实用价值,丢在一边没有理会.
最近,制作多层贴图terrain后,感觉采用自动贴图的方式可以基本解决这个难题,方法是:
先由高度图生成一般的Mesh,再将Mesh在3dmax中任意修改,可扔上去一些石头,峭壁,天桥等,当然,也可以是地洞等.
要把各种材料的过度手工处理好很难,我们采取自动贴图的方式,按面的高度,坡度,阴阳等参数控制多层贴图,这样,
各元件浑然一体,再将Mesh作Octree切割就可以了.(对于地洞类的,我觉得还是采用入口方式单独渲染比较好).
程序文件较多,但各单元都很简单,简要分析如下:
基本文件如下:
DXErrors.cpp //出错处理
FlyDemo.cpp //飞行演示,里面建立terrain,skydome,path
Helper.cpp //不是帮助提示,里面只有一个函数ComputeViewMatrix,计算视口矩阵,(放在这里有点怪怪的)
Material.cpp //材料
MManager.cpp //材料管理器
Octree.cpp //Octree功能单元,对树上各渲染队列的可视化检测,后面注释.
Path.cpp //自动演示路径功能,打开path.dat,文本记录的路径,按路径移动摄影机
RenderQueue.cpp //队列渲染:Octree记录的不是片,而是一个个片的三角形队列,Octree检索后保存可视队列到这里,然后渲染
Skydome.cpp //天空球,与skybox类似,不管它
SplashScreen.cpp //Very简单,封面图象
Terrain.cpp //Terrain单元,很简单,载入切割好的Octree队列,渲染时调用Octree检索可视队列,调用队列渲染绘制,fog的设置
TerrainApp.cpp //主程序,窗口建立,d3d初始化,建立FlyDemo及SplashScreen
Texture.cpp //贴图
TManager.cpp //贴图管理
TriangleList.cpp //三角形列表,由RenderQueue调用,实现最终渲染
VertexStore.cpp //顶点堆,供三角形列表使用
Viewer.cpp //摄影机,内容不多,要点是里面的ComputeClipVolume函数,计算出投影锥的6个平面,供Octree检索使用
核心程序是Octree.cpp,功能也很简单,
//-----------------------------------------------------------------------------
// File: Octree.cpp
//
// Terrain Demo.
//
// Copyright (c) 2000 ATI Technologies Inc. All rights reserved.
//-----------------------------------------------------------------------------
#define STRICT
#define D3D_OVERLOADS
#include
#include
#include "Helper.h"
#include "Octree.h"
#include "DXErrors.h"
COctreeNode::COctreeNode(FILE *fp) //建立
{
ReadTree(fp); //从切割好的文件读取渲染队列内容
}
COctreeNode::~COctreeNode()
{
if (m_pKids[0] != NULL)
{
for(DWORD i = 0; i < 8; i++)
delete m_pKids[i];
}
else
{
//this node is a leaf so just delete its poly lists
delete []m_pTriangleLists;
}
}
VOID COctreeNode::ReadTree(FILE *fp) //读取树文件
{
CHAR type[5];
fread(type, sizeof(CHAR), 4, fp); //4字节头标志
type[4]='\0';
fread(&m_vCenter, sizeof(D3DXVECTOR3), 1, fp); //三个float中心位置
fread(&m_fWidth, sizeof(FLOAT), 1, fp); //1个float宽
fread(&m_fHeight, sizeof(FLOAT), 1, fp); //1个float高
fread(&m_fDepth, sizeof(FLOAT), 1, fp); //1个float深
fread(&m_dwListCount, sizeof(DWORD), 1, fp); //队列数
DWORD i;
FLOAT wd2 = m_fWidth / 2.0f;
FLOAT hd2 = m_fHeight / 2.0f;
FLOAT dd2 = m_fDepth / 2.0f;
for(i = 0; i < 8; i++) //8叉存储,记录每格的中心坐标
{
//compute the location of the current corner
m_vCorners[i].x = m_vCenter.x + ((i & 0x1) ? wd2 : -wd2);
m_vCorners[i].y = m_vCenter.y + ((i & 0x2) ? hd2 : -hd2);
m_vCorners[i].z = m_vCenter.z + ((i & 0x4) ? dd2 : -dd2);
}
if (!strcmp(type, "LEAF")) //如果是叶子,读取并存入面片队列
{
//node is a leaf in the tree read its polygon
//lists and set its children to NULL
m_pTriangleLists = new CTriangleList[m_dwListCount];
for(i = 0; i < m_dwListCount; i++)
m_pTriangleLists[i].Load(fp); //读取面片队列
for(i = 0; i < 8; i++)
{
m_pKids[i] = NULL;
}
}
else if (!strcmp(type, "NODE")) //如是树叉,建立八个分支
{
//the node is non-leaf, so read its children
for(i = 0; i < 8; i++)
{
m_pKids[i] = new COctreeNode(fp); //递归,直到全部读入,Octree读入完成
}
}
}
//Terrain采用Octree,估计应该把内容分成小块区域,使渲染及查询的比例平衡
//每个叶子上保存的是三角队列,相当于一个model.
//不知道在切割时对跨界的队列如何处理,是强行切开或是两边都存.两边都存的话,要设定个检索标志,避免重复渲染.
//下面是Octree可视检测,基本的Octree检索方法.好象算法还比较简单,没有太多的乘法.
VOID COctreeNode::Cull(CRenderQueue *pRQ, CLIPVOLUME& cv, D3DXVECTOR3& pos)
{
DWORD zones[8] = {0, 0, 0, 0, 0, 0, 0, 0};
FLOAT x, y, z;
DWORD i;
//对八叉的某格子,按包围盒8个点作是否在视锥内的检测
for(i = 0; i < 8; i++)
{
x = m_vCorners[i].x;
y = m_vCorners[i].y;
z = m_vCorners[i].z;
//由点到面的距离,可知道是否在视锥的某个平面的外面
if ((cv.pNear.a * x + cv.pNear.b * y + cv.pNear.c * z + cv.pNear.d) > -0.01f) //近平面
zones[i] |= 0x01; //某个在里,做个标记
else if ((cv.pFar.a * x + cv.pFar.b * y + cv.pFar.c * z + cv.pFar.d) > -0.01f) //远平面
zones[i] |= 0x02;
if ((cv.pLeft.a * x + cv.pLeft.b * y + cv.pLeft.c * z + cv.pLeft.d) > -001f)
zones[i] |= 0x04;
else if ((cv.pRight.a * x + cv.pRight.b * y + cv.pRight.c * z + cv.pRight.d) > -0.01f)
zones[i] |= 0x08;
if ((cv.pTop.a * x + cv.pTop.b * y + cv.pTop.c * z + cv.pTop.d) > -0.01f)
zones[i] |= 0x10;
else if ((cv.pBottom.a * x + cv.pBottom.b * y + cv.pBottom.c * z + cv.pBottom.d) > -0.01f)
zones[i] |= 0x20;
}
//if all of the corners are outside of the boundaries
// this node is excluded, so stop traversing
if (zones[0] & zones[1] & zones[2] & zones[3] & zones[4] & zones[5] & zones[6] & zones[7]) //如果8点均在外,剔除
return; //疑问,如果一个大包围盒把视锥完整包围了,不是也被剔除了吗? 看过别的算法是,如果8个点都在某个面的外侧才剔除,
// if this is a leaf add the triangle lists to the render queue
if (m_pKids[0] == NULL)
{
for(i = 0; i < m_dwListCount; i++)
pRQ->AddToQueue(&m_pTriangleLists[i]); //如果是叶子,将叶子上内容保存到渲染队列
}
else
{
//this is not a leaf traverse deeper
for(i = 0; i < 8; i++)
m_pKids[i]->Cull(pRQ, cv, pos); //如果是树叉,按八叉递归查询更细的盒子
}
}
//Octree的切割并不复杂,只要安八叉切割方块,比较三角条的包围盒是否在方块内就可以了.
//对庞大的场景完全采用Octree的方式,是否能达到理想的效率仍表示怀疑.
//虽然ATI的Demo看起来速度理想,我觉得还是需要对不同单元做入口处理比较可靠
//其他单元不复杂,不再注解.有兴趣的可自己看程序,如下地址下载.
http://www.ati.com/developer/sdk/RadeonSDK/Html/Samples/Direct3D/RadeonTerrainDemo.html
反射计算(dx9 StencilMirror)
反射只是翻转摄影机,将场景渲染到Texture上,或使用STENCIL蒙板直接绘制。
具体计算查看了D3D的 Sample,如下:
D3DXMATRIXA16 matViewSaved;
m_pd3dDevice->GetTransform( D3DTS_VIEW, &matViewSaved ); //取得视口
//定义一个翻转的参考平面,可根据需要自己定义位置及法线
D3DXVECTOR3 vPoint(0,0,0);
D3DXVECTOR3 vNormal(0,1,0)
;
D3DXMATRIXA16 matView, matReflect;
D3DXPLANE plane;
D3DXPlaneFromPointNormal( &plane, &vPoint, &vNormal ); //生成这个平面
D3DXMatrixReflect( &matReflect, &plane ); //取得该平面的反射矩阵
D3DXMatrixMultiply( &matView, &matReflect, &matViewSaved ); //使用反射矩阵翻转视口,摄影机被翻转到反射位置
m_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); //设置视口,这样摄影机渲染出的内容就是翻转的了
//设置剪切平面,使反射面上的内容被渲染,面下的被丢弃
m_pd3dDevice->SetClipPlane( 0, plane );
m_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE, 0x01 );
//(不知在Shader里面,这个功能是否还起作用)
//如果渲染到材料上,需要使用投影矩阵把材料贴到实际反射的物体上。也可把物体点位先转换为屏幕坐标再贴图,原理是一样的
Bump 简要注释
Bump原理本来很简单:根据细节图,调整每点的光照,使平面具有凹凸效果.
但查阅了一下,门派很多,算法各有差别,比如,有直接normalize的,因ps1.1不支持,有用Cube图查表法解决normalize,
有用3D图实现SelfShadow的,等,搞得复杂无比,似乎需要研究十天八天的才能搞明白.
下面这个程序是从http://www-user.tu-chemnitz.de/~vix/homepage/找到的一个小Demo,比较单纯.
1.切线的生成
绘制Bump除了需要法线,还需要知道切线,幸好Direct3D为我们准备了切线计算函数,一切就变得很简单了:
if FAILED( D3DXLoadMeshFromX( sFilename, D3DXMESH_SYSTEMMEM, dev, NULL, &pD3DXBuf, NULL, &dwNumMaterials, &pMsh ) )
return E_FAIL;
if (Tangent)
{
D3DVERTEXELEMENT9 decl[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 }, //准备一个描述标
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
{ 0, 32, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TANGENT, 0 },
D3DDECL_END()
};
//reform the mesh to one which will have space for the tangent vectors
if (FAILED(pMsh->CloneMesh(D3DXMESH_MANAGED, decl, dev, &pMsh2)))
return E_FAIL;
SAFE_RELEASE(pMsh); pMsh = pMsh2;
if FAILED(D3DXComputeNormals(pMsh2,NULL))
return E_FAIL;
if FAILED(D3DXomputeTangent(pMsh2,0,0,0,TRUE,NULL))
return E_FAIL;
}
//这样,Mesh的法线和切线就制作好了,可以投入FX去渲染Bump了
2.Offset Bump的Fx
该部分也很简单,只需要VS1.1及PS1.1支持,PS里完全没用到normalize
loat4x4 mWorldViewProj;
float4x4 mWorld;
float3 vEye_Pos;
float3 vLight_Pos;
float3 vFactors = { 0.02, -0.01, 0.5 }; //位移控制参数,
texture tBump < string name = "Data\\rockwall_normal.tga"; >; //细节法线图
texture tHeight < string name = "Data\\rockwall_height.tga"; >; //细节高度图(由高度图可以生成上面的法线图,为了速度快,我们预先分别生成好两个图)
texture tBase < string name = "Data\\rockwall.tga"; >; //表面贴图
sampler sBase = sampler_state
{
Texture = <tBase>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
sampler sBump = sampler_state
{
Texture = <tBump>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
sampler sHeight = sampler_state
{
Texture = <tHeight>;
MipFilter = LINEAR;
MinFilter = LINEAR;
MagFilter = LINEAR;
};
struct OFFSET_VS_OUT
{
float4 Pos: POSITION;
float3 Light: COLOR0;
float3 Normal: COLOR1;
float2 Height: TEXCOORD0;
float3 Base1: TEXCOORD1;
float3 Base2: TEXCOORD2;
float2 Bump: TEXCOORD3;
};
//顶点程序
OFFSET_VS_OUT Offset_VS(float4 inPos: POSITION, //点坐标
float3 inNormal: NORMAL, //法线
float2 inTex: TEXCOORD0, //贴图坐标
float3 inTangent: TANGENT) //输入参数加上切钱
{
OFFSET_VS_OUT Out;
Out.Pos = mul(inPos, mWorldViewProj); //点转换到屏幕空间
Out.Height = inTex;
Out.Bump = inTex;
float3x3 TextureSpace; //制作该点的切线空间转换矩阵,3x3的,不需要平移等参数,不必4x4的
TextureSpace[0] = mul(inTangent, mWorld);
TextureSpace[1] = mul(cross(inTangent, inNormal), mWorld); //除法线,切线外的第三轴,人称Binormal,其实就是法线切线的cross,不需要另准备的
TextureSpace[2] = mul(inNormal, mWorld);
//视线转换到切线空间
float3 Pos = mul(inPos, mWorld);
float3 eyePos = mul(vEye_Pos, mWorld);
float3 eyeDir = normalize(eyePos - Pos); //视点-点坐标=视线
float3 temp = mul(TextureSpace, eyeDir); //视线转换到切线空间
//这里生成了两个base坐标,传递给ps,计算视线的x偏移及y偏移
Out.Base1.x = temp.x * vFactors.x;
Out.Base1.y = temp.x * vFactors.y;
Out.Base1.z = inTex.x;
Out.Base2.x = temp.y * vFactors.x;
Out.Base2.y = temp.y * vFactors.y;
Out.Base2.z = inTex.y;
//灯光转换到切线空间
float3 Light = mul(vLight_Pos, mWorld);
float3 LightDir = normalize(Light - Pos);
Out.Light.xyz = mul(TextureSpace, LightDir) * 0.5 + 0.5; //将正负1规范到0-1
//法线转换到切线空间
float3 Normal = mul(inNormal, mWorld);
Out.Normal = mul(TextureSpace, Normal) * 0.5 + 0.5; //将正负1规范到0-1
return Out;
}
//片段程序
float4 Offset_PS(OFFSET_VS_OUT IN): COLOR
{
float3 height = tex2D(sHeight, IN.Height); //查出高度
//根据高度计算出偏移后的坐标,就是所谓的Parallax
float2 temp;
temp.x = dot(height,IN.Base1); //视线的x偏移
temp.y = dot(height,IN.Base2); //视线的y偏移
float4 base = tex2D(sBase, temp); //取出新位置的贴图
float3 bumpNormal = 2 * (tex2D(sBump,IN.Bump) - 0.5); //取出老位置的Bump法线
float4 diff = saturate(dot(IN.Light * 2 - 1,bumpNormal)); //根据入射光与Bump法线夹角,计算出扰动后的亮度
float shadow = saturate(4 * dot(IN.Normal.xyz * 2 - 1,IN.Light.xyz * 2 - 1)); //由法线和入射光夹角计算出亮度
return base * diff * shadow; //颜色,亮度和扰动后的亮度合成,输出
}
technique OffsetBumpMapping
{
pass Diff
{
VertexShader = compile vs_1_1 Offset_VS();
PixelShader = compile ps_1_1 Offset_PS();
}
}
3.SelfShadow的Horizon_Mapping
上例中没有用到SelfShadow,水平Map的原理和以前文章的Terrain的阴影原理是一样的,预先根据高度图计算出阴影图,渲染时合成上去.
当灯光位置变化时,重新计算阴影图;当然,也可以计算出不同角度的多张阴影图,使用3D图传进来,通过自动插值得到所需要角度的阴影图
这是来自humus.ca的SelfShadow程序中的一小段
#define round(x) (int) (x + 0.5f)
inline unsigned char Pack1(const float x){
return (unsigned char) (255.0f * fabsf(x));
}
inline unsigned char Pack2(const float x){
return (unsigned char) (255.0f * x);
}
void MainApp::createHorizonMap(Image3D *hMap, Image *image, int wt, int ht, int n, float height){
int w = image->getWidth();
int h = image->getHeight();
unsigned char *src = image->getImage();
unsigned char *dest = new unsigned char[wt * ht * n];
hMap->loadFromMemory(dest, wt, ht, n, FORMAT_I8, true, false);
for (int z = 0; z < n; z++){ //按角度分别计算出多幅水平阴影图
float xv = cosf(z * 2 * PI / n); //根据角度计算查询方向
float yv = sinf(z * 2 * PI / n);
float invMax = 1.0f / max(fabsf(xv), fabsf(yv));
xv *= invMax;
yv *= invMax;
float len = sqrtf(xv * xv + yv * yv);
for (int y = 0; y < ht; y++){ .//遍历图上所有点
for (int x = 0; x < wt; x++){
float maxAng = 0;
float xp = float(x * w) / wt;
float yp = float(y * h) / ht;
float bh = float(src[int(yp) * w + int(xp)]); //取得高度
float dist = 0;
//do {
for (unsigned int i = 0; i < 256; i++){ //按光线方向查询是否有更高的点,有就是阴影,没有不是阴影
xp += xv; //为什么查询256点?
yp += yv;
dist += len;
int xt = round(xp); //图片越界循环
int yt = round(yp);
while (xt < 0) xt += w;
while (yt < 0) yt += h;
xt %= w;
yt %= h;
//if (xt < 0 || xt >= w || yt < 0 || yt >= h) break;
float ang = (float(src[yt * w + xt]) - bh) / dist; //计算投影高度
if (ang > maxAng) maxAng = ang;
}
//} while (true);
*dest++ = Pack2(sinf(atanf(height * maxAng)));
nbsp; }
}
}
}
在ps中查询也很简单:
float horizon = tex3D(HorizonMap, float3(newTexCoord, e)); //从3d图查询阴影
其中的e为灯光的方向
软阴影Soft Shadows程序注释
//软阴影Soft Shadows程序注释
//这是一个效果不错的模糊边缘阴影的ShadowMap的Demo,网上能找到中文说明。
//原始程序可在下列地址下载:
http://www.gamedev.net/reference/articles/article2193.asp

/*-----------------------------------------------------------------------------
Name : Soft Shadows.cpp
Desc : Main source file.
Author : Anirudh S Shastry. Copyright (c) 2004.
Date : 22nd June, 2004.
-----------------------------------------------------------------------------*/
//其原理是,先按灯光位置渲染出ShadowMap,这和其他的一样。
//再从摄影机位置,渲染场景阴影图(只要黑白的阴影图)。阴影图选择屏幕的1/4大小,阴影图比较小,速度比较快。//对阴影图进行模糊处理,这是2D处理,横向一次,纵向一次
//再次绘制场景,把模糊完的阴影图贴上去(当然不是直接贴,而是边绘制变查找屏幕坐标位置的模糊后的阴影值,
//这样,场景就有过度平滑的阴影,避免了ShadowMap锯齿的问题
//这个方法有两个问题,
//一是场景使用了三次渲染(一次ShadowMap,一次阴影,最后一次完整渲染,另外,模糊材料渲染二次),对速度有影响,
//但比较别的方法的64点的查询融合要快。
//另一个问题是模糊算法导致阴影移位,这样,场景渲染可能会把阴影投到错误的位置:应该采取一种不移位的模糊方式,即在阴影范围
//内进行过渡处理,这应该可以做到
//如果做到这两点,应该可以做出效果和速度都比较理想的阴影。
//本注释对程序作了几处优化.
//1.绘制阴影图使用白色背景,这样可以避免把未绘制的空部分当成阴影.
//2.阴影图幅面比场景幅面增加部分,避免模糊处理边缘取不到样.在使用阴影图时校正对准正确区域.
#define STRICT
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <d3d9.h>
#include <d3dx9.h>
#include <mmsystem.h>
#include "resource.h"
#define SHADOW_MAP_SIZE 512
#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768
#define SAFE_RELEASE( x ) { if( x ) { x->Release(); x = NULL; } }
//
// Gaussian functions
//
//模糊处理的参数计算
float GetGaussianDistribution( float x, float y, float rho ) {
float g = 1.0f / sqrt( 2.0f * 3.141592654f * rho * rho );
return g * exp( -(x * x + y * y) / (2 * rho * rho) );
}
void GetGaussianOffsets( bool bHorizontal, D3DXVECTOR2 vViewportTexelSize,
D3DXVECTOR2* vSampleOffsets, float* fSampleWeights ) {
// Get the center texel offset and weight
fSampleWeights[0] = 1.0f * GetGaussianDistribution( 0, 0, 2.0f );
vSampleOffsets[0] = D3DXVECTOR2( 0.0f, 0.0f );
// Get the offsets and weights for the remaining taps
if( bHorizontal ) {
for( int i = 1; i < 15; i += 2 ) {
vSampleOffsets[i + 0] = D3DXVECTOR2( i * vViewportTexelSize.x, 0.0f );
vSampleOffsets[i + 1] = D3DXVECTOR2( -i * vViewportTexelSize.x, 0.0f );
fSampleWeights[i + 0] = 2.0f * GetGaussianDistribution( float(i + 0), 0.0f, 3.0f );
fSampleWeights[i + 1] = 2.0f * GetGaussianDistribution( float(i + 1), 0.0f, 3.0f );
}
}
else {
for( int i = 1; i < 15; i += 2 ) {
vSampleOffsets[i + 0] = D3DXVECTOR2( 0.0f, i * vViewportTexelSize.y );
vSampleOffsets[i + 1] = D3DXVECTOR2( 0.0f, -i * vViewportTexelSize.y );
fSampleWeights[i + 0] = 2.0f * GetGaussianDistribution( 0.0f, float(i + 0), 3.0f );
fSampleWeights[i + 1] = 2.0f * GetGaussianDistribution( 0.0f, float(i + 1), 3.0f );
}
}
}
//-----------------------------------------------------------------------------
// 一些变量设置 Global variables
//-----------------------------------------------------------------------------
HWND g_hWnd = NULL;
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL;
LPD3DXFONT g_pFont = NULL;
LPD3DXMESH g_pScene = NULL;
LPD3DXEFFECT g_pEffect = NULL;
LPDIRECT3DVERTEXBUFFER9 g_pQuadVB = NULL;
LPDIRECT3DTEXTURE9 g_pColorMap_Floor = NULL;
LPDIRECT3DTEXTURE9 g_pColorMap_Statue = NULL;
LPDIRECT3DTEXTURE9 g_pSpotMap = NULL;
LPDIRECT3DTEXTURE9 g_pShadowMap = NULL;
LPDIRECT3DSURFACE9 g_pShadowSurf = NULL;
LPDIRECT3DSURFACE9 g_pShadowDepth = NULL;
LPDIRECT3DTEXTURE9 g_pScreenMap = NULL;
LPDIRECT3DSURFACE9 g_pScreenSurf = NULL;
LPDIRECT3DTEXTURE9 g_pBlurMap[2] = {NULL};
LPDIRECT3DSURFACE9 g_pBlurSurf[2] = {NULL};
LPDIRECT3DSURFACE9 g_pNewDepthRT = NULL;
LPDIRECT3DSURFACE9 g_pOldColorRT = NULL;
LPDIRECT3DSURFACE9 g_pOldDepthRT = NULL;
D3DXVECTOR2 g_vSampleOffsets[15];
FLOAT g_fSampleWeights[15];
LPDIRECT3DSURFACE9 g_pScreenshot = NULL;
UINT g_uNumScreenshots = 0;
UINT g_uFrameCount = 0;
FLOAT g_fStartTime = 0.0f;
FLOAT g_fFramerate = 0.0f;
FLOAT g_fStopTime = 0.0f;
BOOL g_bDisplayStats = TRUE;
D3DXVECTOR3 g_vEyePos = D3DXVECTOR3( -20.0f, 20.0f, -20.0f );
D3DXVECTOR3 g_vEyeAim = D3DXVECTOR3( 20.0f, 0.0f, 20.0f );
D3DXVECTOR3 g_vUp = D3DXVECTOR3( 0.0f,nbsp; 1.0f, 0.0f );
BOOL g_bWindowed = FALSE;
BOOL g_bWireframe = FALSE;
BOOL g_bSoftShadows = TRUE;
BOOL g_bFiltered = TRUE;
// Vertex element
D3DVERTEXELEMENT9 dwElement[] =
{
{ 0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
{ 0, 12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0 },
{ 0, 24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
D3DDECL_END()
};
struct QuadVertex
{
D3DXVECTOR4 p;
D3DXVECTOR2 t;
};
#define FVF_QUADVERTEX (D3DFVF_XYZRHW | D3DFVF_TEX1)
//-----------------------------------------------------------------------------
// 主程序入口 Functions
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT Initialize();
HRESULT ShutDown();
HRESULT Render();
HRESULT FrameMove();
HRESULT ScreenGrab();
//-----------------------------------------------------------------------------
// Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow )
{
WNDCLASSEX winClass;
MSG uMsg;
memset(&uMsg,0,sizeof(uMsg));
winClass.lpszClassName = "MY_WINDOWS_CLASS";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = WindowProc;
winClass.hInstance = hInstance;
winClass.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_ICON1);
winClass.hIconSm = LoadIcon(hInstance, (LPCTSTR)IDI_ICON1);
winClass.hCursor = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName = (LPCSTR)IDR_MENU1;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
if( RegisterClassEx( &winClass) == 0 )
return E_FAIL;
if( g_bWindowed )
g_hWnd = CreateWindowEx( NULL, "MY_WINDOWS_CLASS", "Soft Shadows", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );
else
g_hWnd = CreateWindowEx( NULL, "MY_WINDOWS_CLASS", "Soft Shadows", WS_POPUP | WS_SYSMENU | WS_VISIBLE,
0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );
if( g_hWnd == NULL )
return E_FAIL;
ShowWindow( g_hWnd, nCmdShow );
UpdateWindow( g_hWnd );
if( FAILED( Initialize() ) )
{
MessageBox( g_hWnd, "Unable to initialize Direct3D!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Get the starting time
g_fStartTime = timeGetTime() * 0.001f;
while( uMsg.message != WM_QUIT )
{
if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &uMsg );
DispatchMessage( &uMsg );
}
else
{
FrameMove();
Render();
g_uFrameCount++;
g_fFramerate = (FLOAT)g_uFrameCount / (timeGetTime() * 0.001f - g_fStartTime);
}
}
// Get the stopping time
g_fStopTime = timeGetTime() * 0.001f;
if( FAILED( ShutDown() ) )
{
MessageBox( g_hWnd, "Unable to shutdown Direct3D!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );
return uMsg.wParam;
}
//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc( HWND hWnd,
UINT msg,
WPARAM wParam,
LPARAM lParam )
{
switch( msg )
{
case WM_KEYDOWN:
{
switch( wParam )
{
case VK_ESCAPE:
PostQuitMessage(0);
break;
case VK_UP:
{
D3DXVECTOR3 vView;
D3DXVec3Normalize( &vView, &(g_vEyeAim - g_vEyePos) );
g_vEyePos.x += vView.x;
g_vEyePos.z += vView.z;
g_vEyeAim.x += vView.x;
g_vEyeAim.z += vView.z;
break;
}
case VK_DOWN:
{
D3DXVECTOR3 vView;
D3DXVec3Normalize( &vView, &(g_vEyeAim - g_vEyePos) );
g_vEyePos.x -= vView.x;
g_vEyePos.z -= vView.z;
g_vEyeAim.x -= vView.x;
g_vEyeAim.z -= vView.z;
break;
}
case VK_LEFT:
{
D3DXVECTOR3 vView, vStrafe;
D3DXVec3Normalize( &vView, &(g_vEyeAim - g_vEyePos) );
D3DXVec3Cross( &vStrafe, &vView, &g_vUp );
g_vEyePos.x += vStrafe.x;
g_vEyePos.z += vStrafe.z;
g_vEyeAim.x += vStrafe.x;
g_vEyeAim.z += vStrafe.z;
break;
}
 
case VK_RIGHT:
{
D3DXVECTOR3 vView, vStrafe;
D3DXVec3Normalize( &vView, &(g_vEyeAim - g_vEyePos) );
D3DXVec3Cross( &vStrafe, &vView, &g_vUp );
g_vEyePos.x -= vStrafe.x;
g_vEyePos.z -= vStrafe.z;
g_vEyeAim.x -= vStrafe.x;
g_vEyeAim.z -= vStrafe.z;
break;
}
case 'Z':
{
g_vEyePos.y += 1.0f;
break;
}
case 'X':
{
g_vEyePos.y -= 1.0f;
break;
}
case 'W':
{
g_bWireframe = !g_bWireframe;
break;
}
case 'S':
{
g_bSoftShadows = !g_bSoftShadows;
break;
}
case 'F':
{
g_bFiltered = !g_bFiltered;
break;
}
case 'G':
{
ScreenGrab();
break;
}
case VK_F1:
{
g_bDisplayStats = !g_bDisplayStats;
}
}
}
break;
case WM_CLOSE:
{
MessageBox( g_hWnd, "By Anirudh S Shastry. Copyright (c) 2004.", "About", MB_OK );
PostQuitMessage(0);
}
case WM_DESTROY:
{
PostQuitMessage(0);
}
break;
case WM_COMMAND:
{
switch( LOWORD(wParam) )
{
case ID_FILE_EXIT:
{
SendMessage( g_hWnd, WM_CLOSE, 0, 0 );
return 0;
}
case ID_HELP_ABOUT:
{
MessageBox( g_hWnd, "By Anirudh S Shastry. Copyright (c) 2004.", "About", MB_OK );
return 0;
}
case ID_FILE_TOGGLESTATS:
{
g_bDisplayStats = !g_bDisplayStats;
return 0;
}
default:
{
break;
}
}
break;
}
default:
{
return DefWindowProc( hWnd, msg, wParam, lParam );
}
break;
}
return 0;
}
//-----------------------------------------------------------------------------
// Name: Initialize()
// Desc: Initialize Direct3D
//-----------------------------------------------------------------------------
HRESULT Initialize()
{
// Create the Direct3D object
g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
if( g_pD3D == NULL )
{
MessageBox( g_hWnd, "Unable to create the Direct3D object!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Display mode
D3DDISPLAYMODE d3ddm;
// Get the suitable display mode
UINT uNumModes = g_pD3D->GetAdapterModeCount( D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8 );
// Select the best mode (offering the highest frequency at the specified resolution)
D3DDISPLAYMODE* pModes = new D3DDISPLAYMODE[uNumModes];
for( UINT uMode = 0; uMode < uNumModes; uMode++ )
{
// Enumerate the adapter modes
if( FAILED( g_pD3D->EnumAdapterModes( D3DADAPTER_DEFAULT, D3DFMT_X8R8G8B8, uMode, &pModes[uMode] ) ) )
{
MessageBox( g_hWnd, "Unable to enumerate adapter display mode!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
}
for( uMode = 0; uMode < uNumModes; uMode++ )
{
if( pModes[uMode].Width == SCREEN_WIDTH && pModes[uMode].Height == SCREEN_HEIGHT )
{
if( pModes[uMode + 1].Width == SCREEN_WIDTH && pModes[uMode + 1].Height == SCREEN_HEIGHT )
{
if( pModes[uMode].RefreshRate > pModes[uMode + 1].RefreshRate )
{
d3ddm = pModes[uMode];
}
else
{
d3ddm = pModes[uMode + 1];
}
}
else
{
d3ddm = pModes[uMode];
}
}
else if( pModes[uMode].Width > SCREEN_WIDTH || pModes[uMode].Height > SCREEN_HEIGHT ) {
break;
}
}
// Delete the list
delete [] pModes;
if( FAILED( g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
d3ddm.Format, D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )
{
MessageBox( g_hWnd, "Unable to find suitable display mode!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// If we are using windowed mode
if( g_bWindowed )
g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm );
if( FAILED( g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
d3ddm.Format, D3DUSAGE_DEPTHSTENCIL,
D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )
{
MessageBox( g_hWnd, "Unable to get adapter display mode!", ERROR, MB_OK | MB_ICONERROR );
retrn E_FAIL;
}
// Get the hardware caps
D3DCAPS9 d3dCaps;
if( FAILED( g_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, &d3dCaps ) ) )
{
MessageBox( g_hWnd, "Unable to get hardware caps!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
//需要shader2.0 支持
// Check for vertex/pixel shaders 2.0 support
if( d3dCaps.VertexShaderVersion < D3DVS_VERSION( 2, 0 ) || d3dCaps.PixelShaderVersion < D3DPS_VERSION( 2, 0 ) )
{
MessageBox( g_hWnd, "Vertex/Pixel shaders 2.0 not supported!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
//使用32位图象格式,以提高深度测试精度
// Check for R32F surface format support
if( FAILED( g_pD3D->CheckDepthStencilMatch( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, d3ddm.Format,
D3DFMT_R32F, D3DFMT_D16 ) ) )
{
MessageBox( g_hWnd, "R32F format not supported!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
DWORD dwBehaviorFlags = 0;
if( d3dCaps.VertexProcessingCaps != 0 )
dwBehaviorFlags = D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE;
else
dwBehaviorFlags = D3DCREATE_SOFTWARE_VERTEXPROCESSING;
D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));
d3dpp.BackBufferWidth = d3ddm.Width;
d3dpp.BackBufferHeight = d3ddm.Height;
d3dpp.BackBufferFormat = d3ddm.Format;
d3dpp.BackBufferCount = 1;
d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.hDeviceWindow = g_hWnd;
d3dpp.Windowed = g_bWindowed;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.Flags = D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL;
if( g_bWindowed )
{
d3dpp.FullScreen_RefreshRateInHz = 0;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
}
else
{
d3dpp.FullScreen_RefreshRateInHz = d3ddm.RefreshRate;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;
}
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
dwBehaviorFlags, &d3dpp, &g_pd3dDevice ) ) )
{
MessageBox( g_hWnd, "Unable to create the Direct3D device!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Create the font object
if( FAILED( D3DXCreateFont( g_pd3dDevice, 16, 0, FW_BOLD, 1, false, DEFAULT_CHARSET,
OUT_TT_ONLY_PRECIS, 0, 0, "Veranda", &g_pFont ) ) )
{
MessageBox( g_hWnd, "Unable to create the font object!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Load the scene
//载入女神雕象模型
LPD3DXMESH pTempScene = NULL;
if( FAILED( D3DXLoadMeshFromX( "Media/Soft Shadows.x", D3DXMESH_32BIT, g_pd3dDevice,
NULL, NULL, NULL, NULL, &pTempScene ) ) )
{
MessageBox( g_hWnd, "Unable to load Soft Shadows.x", "Error", MB_OK | MB_ICONERROR );
return E_FAIL;
}
//复制为32位格式
pTempScene->CloneMesh( D3DXMESH_32BIT, dwElement, g_pd3dDevice, &g_pScene );
SAFE_RELEASE( pTempScene );
// Compute the normals and tangents for the scene
D3DXComputeNormals( g_pScene, NULL ); //计算模型法线
// Create the effect
//效果文件为 Media/Soft Shadows.fx
LPD3DXBUFFER pBufferErrors = NULL;
if( FAILED( D3DXCreateEffectFromFile( g_pd3dDevice, "Media/Soft Shadows.fx", NULL, NULL, 0,
NULL, &g_pEffect, &pBufferErrors ) ) )
{
LPVOID pErrors = pBufferErrors->GetBufferPointer();
MessageBox(NULL, (const char*)pErrors, ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
SAFE_RELEASE( pBufferErrors );
// Create the quad vertex buffer
//建立一个方片,做模糊绘制用
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 4 * sizeof(QuadVertex), D3DUSAGE_WRITEONLY, FVF_QUADVERTEX,
D3DPOOL_DEFAULT, &g_pQuadVB, NULL ) ) )
{
MessageBox( g_hWnd, "Unable to create vertex buffer!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Copy the vertices
QuadVertex* pVertices;
//方片为屏幕大小
g_pQuadVB->Lock( 0, 0, (void**)&pVertices, 0 );
pVertices[0].p = D3DXVECTOR4( 0.0f, 0.0f, 0.0f, 1.0f );
pVertices[1].p = D3DXVECTOR4( 0.0f, SCREEN_HEIGHT / 2, 0.0f, 1.0f );
pVertices[2].p = D3DXVECTOR4( SCREEN_WIDTH / 2, 0.0f, 0.0f, 1.0f );
pVertices[3].p = D3DXVECTOR4( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 0.0f, 1.0f );
pVertices[0].t = D3DXVECTOR2( 0.0f, 0.0f );
pVertices[1].t = D3DXVECTOR2( 0.0f, 1.0f );
pVertices[2].t = D3DXVECTOR2( 1.0f, 0.0f );
pVertices[3].t = D3DXVECTOR2( 1.0f, 1.0f );
g_pQuadVB->Unlock();
// Load the color and spot maps
//地板材料
if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "Media/Color_Floor.dds", &g_pColorMap_Floor ) ) )
{
MessageBox( g_hWnd, "Unable to load Color_Floor.dds", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
//模型材料
if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "Media/Color_Statue.dds", &g_pColorMap_Statue ) ) )
{
MessageBox( g_hWnd, "Unable to load Color_Statue.dds", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
//灯光光罩图象
if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, "Media/SpotMap.dds", &g_pSpotMap ) ) )
{
MessageBox( g_hWnd, "Unable to load SpotMap.dds", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Create the shadow map,这里建立的阴影图像材料,为32位格式的,以提高精度
if( FAILED( g_pd3dDevice->CreateTexture( SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 1, D3DUSAGE_RENDERTARGET,
// D3DFMT_D32F_LOCKABLE,
D3DFMT_R32F,
D3DPOOL_DEFAULT, &g_pShadowMap, NULL ) ) )
{
MessageBox g_hWnd, "Unable to create shadow map!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Grab the texture's surface
g_pShadowMap->GetSurfaceLevel( 0, &g_pShadowSurf );
// Create the screen-sized buffer map
//建立一个屏幕大小的材料,供渲染阴影图使用
if( FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 1, D3DUSAGE_RENDERTARGET,
D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_pScreenMap, NULL ) ) )
{
MessageBox( g_hWnd, "Unable to create screen map!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Grab the texture's surface
g_pScreenMap->GetSurfaceLevel( 0, & g_pScreenSurf );
//建立两个屏幕大小的材料,作水平垂直两次模糊阴影处理
//其实并不需要屏幕大小,512x512足够,Linear拉伸会自动平滑处理,
// Create the blur maps
for( int i = 0; i < 2; i++ )
{
if( FAILED( g_pd3dDevice->CreateTexture( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 1, D3DUSAGE_RENDERTARGET,
D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &g_pBlurMap[i], NULL ) ) )
{
MessageBox( g_hWnd, "Unable to create blur map!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
// Grab the texture's surface
g_pBlurMap[i]->GetSurfaceLevel( 0, & g_pBlurSurf[i] );
}
//建立渲染阴影的深度缓存
// Create the shadow depth surface
if( FAILED( g_pd3dDevice->CreateDepthStencilSurface( SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, D3DFMT_D16,
D3DMULTISAMPLE_NONE, 0, TRUE, &g_pShadowDepth, NULL ) ) )
{
MessageBox( g_hWnd, "Unable to create shadow depth surface!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
//建立屏幕大小的的深度缓存,供模糊处理用
// Create the general depth surface
if( FAILED( g_pd3dDevice->CreateDepthStencilSurface( SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, D3DFMT_D16,
D3DMULTISAMPLE_NONE, 0, TRUE, &g_pNewDepthRT, NULL ) ) )
{
MessageBox( g_hWnd, "Unable to create general depth surface!", ERROR, MB_OK | MB_ICONERROR );
return E_FAIL;
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ShutDown()
// Desc: ShutDown Direct3D
//-----------------------------------------------------------------------------
//结束处理
HRESULT ShutDown()
{
// Clean up
SAFE_RELEASE( g_pFont );
SAFE_RELEASE( g_pScene );
SAFE_RELEASE( g_pEffect );
SAFE_RELEASE( g_pColorMap_Floor );
SAFE_RELEASE( g_pColorMap_Statue );
SAFE_RELEASE( g_pSpotMap );
SAFE_RELEASE( g_pShadowMap );
SAFE_RELEASE( g_pShadowSurf );
SAFE_RELEASE( g_pShadowDepth );
SAFE_RELEASE( g_pScreenMap );
SAFE_RELEASE( g_pScreenSurf );
SAFE_RELEASE( g_pBlurMap[0] );
SAFE_RELEASE( g_pBlurSurf[0] );
SAFE_RELEASE( g_pBlurMap[1] );
SAFE_RELEASE( g_pBlurSurf[1] );
SAFE_RELEASE( g_pNewDepthRT );
SAFE_RELEASE( g_pOldColorRT );
SAFE_RELEASE( g_pOldDepthRT );
SAFE_RELEASE( g_pd3dDevice );
SAFE_RELEASE( g_pD3D );
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: FrameMove()
// Desc: Setup effect variables and update stuff
//-----------------------------------------------------------------------------
//场景移动处理,没有什么要说的
HRESULT FrameMove()
{
//
// Camera space matrices
//
// Computee the world matrix
D3DXMATRIX matWorld;
D3DXMatrixIdentity( &matWorld );
// Compute the view matrix
D3DXMATRIX matView;
D3DXMatrixLookAtLH( &matView, &g_vEyePos, &g_vEyeAim, &g_vUp );
// Compute the projection matrix
D3DXMATRIX matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian(60.0f), (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, 1.0f, 1024.0f );
// Compute the world-view-projection matrix
D3DXMATRIX matWorldViewProj = matWorld * matView * matProj;
// World inverse matrix
D3DXMATRIX matWorldIT;
D3DXMatrixInverse( &matWorldIT, NULL, &matWorld );
D3DXMatrixTranspose( &matWorldIT, &matWorldIT );
//
// Light space matrices
//
// View matrix
D3DXVECTOR3 vLightPos = D3DXVECTOR3( 40.0f, 40.0f, -40.0f );
D3DXVECTOR3 vLightAim = D3DXVECTOR3( 0.0f, 0.0f, 0.0f );
D3DXMatrixLookAtLH( &matView, &vLightPos, &vLightAim, &g_vUp );
// Compute the projection matrix
D3DXMatrixOrthoLH( &matProj, 45.0f, 45.0f, 1.0f, 1024.0f );
// Compute the light-view-projection matrix
D3DXMATRIX matLightViewProj = matWorld * matView * matProj;
// Compute the texture matrix
float fTexOffs = 0.5 + (0.5 / (float)SHADOW_MAP_SIZE);
D3DXMATRIX matTexAdj( 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
fTexOffs, fTexOffs, 0.0f, 1.0f );
D3DXMATRIX matTexture = matLightViewProj * matTexAdj;
// Setup the effect variables
g_pEffect->SetMatrix( "g_matWorldViewProj", &matWorldViewProj );
g_pEffect->SetMatrix( "g_matLightViewProj", &matLightViewProj );
g_pEffect->SetMatrix( "g_matWorld", &matWorld );
g_pEffect->SetMatrix( "g_matWorldIT", &matWorldIT );
g_pEffect->SetMatrix( "g_matTexture", &matTexture );
g_pEffect->SetVector( "g_vLightPos", (D3DXVECTOR4*)&vLightPos );
g_pEffect->SetVector( "g_vEyePos", (D3DXVECTOR4*)&g_vEyePos );
g_pEffect->SetVector( "g_vLightColor", &D3DXVECTOR4( 1.0f, 1.0f, 1.0f, 0.5f ) );
g_pEffect->SetBool("g_bFiltered", g_bFiltered);
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: Render()
// Desc: Render the frame
//-----------------------------------------------------------------------------
//渲染场景
HRESULT Render()
{
// Clear the viewport
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0 );
// Begin rendering the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Grab the old render target and depth buffer
g_pd3dDevice->GetRenderTarget( 0, &g_pOldColorRT );
g_pd3dDevice->GetDepthStencilSurface( &g_pOldDepthRT );
//首先渲染ShadowMap深度图,注意,该FX把深度图计算到32位的前景里,不是深度缓存
// Render the scenedepth to the shadow map
g_pd3dDevice->SetRenderTarget( 0, g_pShadowSurf );
g_pd3dDevice->SetDepthStencilSurface( g_pShadowDepth );
// Clear the viewport
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xFFFFFFFF, 1.0f, 0 );
// Set the technique
g_pEffect->SetTechnique( "techShadow" );
// Render the effect
UINT uPasses = 0;
g_pEffect->Begin( &uPasses, 0 );
for( UINT uPass = 0; uPass < uPasses; uPass++ )
{
// Set the current pass
g_pEffect->BeginPass( uPass );
// Draw the floor
g_pScene->DrawSubset(0);
// Draw the statue
g_pScene->DrawSubset(1);
g_pScene->DrawSubset(2);
// End the current pass
g_pEffect->EndPass();
}
// End the effect
g_pEffect->End();
//阴影图渲染完成
//第二步,渲染场景,产生阴影图
if( g_bSoftShadows )
{
// Render the shadowed scene into the screen map
g_pd3dDevice->SetRenderTarget( 0, g_pScreenSurf );
g_pd3dDevice->SetDepthStencilSurface( g_pNewDepthRT );
// Clear the viewport
//这里清除背景原使用0,应该使用白色,避免把空出的部分当成阴影!
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0 );
// Set the technique
g_pEffect->SetTechnique( "techUnlit" ); //这个Tech只渲染阴影
// Set the textures
g_pEffect->SetTexture( "tShadowMap", g_pShadowMap ); //将阴影map传进去
// Render the effect
uPasses = 0;
g_pEffect->Begin( &uPasses, 0 );
for( uPass = 0; uPass < uPasses; uPass++ )
{
// Set the current pass
g_pEffect->BeginPass( uPass );
// Draw the floor
g_pScene->DrawSubset(0);
// Draw the statue
g_pScene->DrawSubset(1);
g_pScene->DrawSubset(2);
// End the current pass
g_pEffect->EndPass();
}
// End the effect
g_pEffect->End();
//阴影图渲染完成
//第三步,对阴影图进行模糊处理,绘制到另一个材料上,包括水平及垂直两次模糊
//水平模糊处理
// Blur the screen map horizontally
g_pd3dDevice->SetRenderTarget( 0, g_pBlurSurf[0] ); //模糊到g_pBlurSurf[0]上
g_pd3dDevice->SetDepthStencilSurface( g_pNewDepthRT ); //深度缓存重复利用
// Clear the viewport
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0 );
// Set the technique
g_pEffect->SetTechnique( "techBlurH" ); //这是模糊的tech
// Compute the Gaussian offsets 计算模糊的距离
GetGaussianOffsets(TRUE, D3DXVECTOR2(1.0f / (FLOAT)SHADOW_MAP_SIZE, 1.0f / (FLOAT)SHADOW_MAP_SIZE),
g_vSampleOffsets, g_fSampleWeights);
g_pEffect->SetValue("g_vSampleOffsets", g_vSampleOffsets, 15 * sizeof(D3DXVECTOR2));
g_pEffect->SetValue("g_fSampleWeights", g_fSampleWeights, 15 * sizeof(FLOAT));
// Set the textures
g_pEffect->SetTexture( "tScreenMap", g_pScreenMap ); //设置刚才的阴影图作为原图
// Render the effect
uPasses = 0;
g_pEffect->Begin( &uPasses, 0 );
for( uPass = 0; uPass < uPasses; uPass++ )
{
// Set the current pass
g_pEffect->BeginPass( uPass );
// Draw the quad
g_pd3dDevice->SetStreamSource( 0, g_pQuadVB, 0, sizeof(QuadVertex) );
g_pd3dDevice->SetFVF( FVF_QUADVERTEX );
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); //绘制矩形,FX负责模糊
// End the current pass
g_pEffect->EndPass();
}
// End the effect
g_pEffect->End();
//下面做纵向模糊
// Blur the screen map vertically
g_pd3dDevice->SetRenderTarget( 0, g_pBlurSurf[1] ); //模糊到材料2
g_pd3dDevice->SetDepthStencilSurface( g_pNewDepthRT );
// Clear the viewport
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, 0 );
// Set the technique
g_pEffect->SetTechnique( "techBlurV" ); //使用刚才水平模糊的结果,继续模糊
// Compute the Gaussian offsets
GetGaussianOffsets(FALSE, D3DXVECTOR2(1.0f / (FLOAT)SHADOW_MAP_SIZE, 1.0f / (FLOAT)SHADOW_MAP_SIZE),
g_vSampleOffsets, g_fSampleWeights);
g_pEffect->SetValue("g_vSampleOffsets", g_vSampleOffsets, 15 * sizeof(D3DXVECTOR2));
g_pEffect->SetValue("g_fSampleWeights", g_fSampleWeights, 15 * sizeof(FLOAT));
// Set the textures
g_pEffect->SetTexture( "tBlurHMap", g_pBlurMap[0] );
// Render the effect
uPasses = 0;
g_pEffect->Begin( &uPasses, 0 );
for( uPass = 0; uPass < uPasses; uPass++ )
{
// Set the current pass
g_pEffect->BeginPass( uPass );
// Draw the quad
g_pd3dDevice->SetStreamSource( 0, g_pQuadVB, 0, sizeof(QuadVertex) );
g_pd3dDevice->SetFVF( FVF_QUADVERTEX );
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2 ); //也是绘制一个方形
// End the current pass
g_pEffect->EndPass();
}
// End the effect
g_pEffect->End();
//阴影图绘制完成
//最后,第四步,绘制场景,PS根据绘点的位置,查询阴影图同位置的颜色,将模糊完的阴影复合进来
// Restore the render target and depth buffer
g_pd3dDevice->SetRenderTarget( 0, g_pOldColorRT );
g_pd3dDevice->SetDepthStencilSurface( g_pOldDepthRT );
SAFE_RELEASE( g_pOldColorRT );
nbsp; SAFE_RELEASE( g_pOldDepthRT );
// Finally, render the shadowed scene
g_pEffect->SetTechnique( "techScene" );
// Set the textures
g_pEffect->SetTexture( "tBlurVMap", g_pBlurMap[1] ); //使用两次模糊的最后结果
g_pEffect->SetTexture( "tSpotMap", g_pSpotMap ); //加灯光光罩
g_pEffect->SetTexture( "tScreenMap", g_pScreenMap ); //设置刚才的阴影图作为原图
// Render the effect
uPasses = 0;
g_pEffect->Begin( &uPasses, 0 );
// Do we need to render the scene in wireframe mode
if( g_bWireframe )
g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
for( uPass = 0; uPass < uPasses; uPass++ )
{
// Set the current pass
g_pEffect->BeginPass( uPass );
// Draw the floor
g_pEffect->SetTexture( "tColorMap", g_pColorMap_Floor );
g_pEffect->CommitChanges();
g_pScene->DrawSubset(0);
// Draw the statue
g_pEffect->SetTexture( "tColorMap", g_pColorMap_Statue );
g_pEffect->CommitChanges();
g_pScene->DrawSubset(1);
g_pScene->DrawSubset(2);
// End the current pass
g_pEffect->EndPass();
}
// End the effect
g_pEffect->End();
// Restore the render state
g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
//渲染完成,OK了
}
else
{
//不使用阴影的渲染,不去管它
// Restore the render target and depth buffer
g_pd3dDevice->SetRenderTarget( 0, g_pOldColorRT );
g_pd3dDevice->SetDepthStencilSurface( g_pOldDepthRT );
SAFE_RELEASE( g_pOldColorRT );
SAFE_RELEASE( g_pOldDepthRT );
// Otherwise, render the scene with hard shadows
g_pEffect->SetTechnique( "techSceneHard" );
// Set the textures
g_pEffect->SetTexture( "tShadowMap", g_pShadowMap );
g_pEffect->SetTexture( "tSpotMap", g_pSpotMap );
// Render the effect
uPasses = 0;
g_pEffect->Begin( &uPasses, 0 );
// Do we need to render the scene in wireframe mode
if( g_bWireframe )
g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
for( uPass = 0; uPass < uPasses; uPass++ )
{
// Set the current pass
g_pEffect->BeginPass( uPass );
// Draw the floor
g_pEffect->SetTexture( "tColorMap", g_pColorMap_Floor );
g_pEffect->CommitChanges();
g_pScene->DrawSubset(0);
// Draw the statue
g_pEffect->SetTexture( "tColorMap", g_pColorMap_Statue );
g_pEffect->CommitChanges();
g_pScene->DrawSubset(1);
g_pScene->DrawSubset(2);
// End the current pass
g_pEffect->EndPass();
}
// End the effect
g_pEffect->End();
// Restore the render state
g_pd3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
}
//绘制面板文字,不管它
// Display the frame stats
if( g_bDisplayStats )
{
TCHAR szStats[255];
sprintf( szStats, "Framerate : %2.1f", g_fFramerate );
RECT rc = { 5, 5, 0, 0 };
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_CALCRECT, 0xFFFFAA00 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_NOCLIP, 0xFFFFAA00 );
sprintf( szStats, "Use arrow and Z, X keys to move around the scene" );
SetRect( &rc, 5, SCREEN_HEIGHT - 40, 0, 0 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_CALCRECT, 0xFFFFAA00 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_NOCLIP, 0xFFFFAA00 );
sprintf( szStats, "Press W to toggle wireframe display" );
SetRect( &rc, 5, SCREEN_HEIGHT - 20, 0, 0 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_CALCRECT, 0xFFFFAA00 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_NOCLIP, 0xFFFFAA00 );
sprintf( szStats, "Press S to toggle soft shadows" );
SetRect( &rc, 350, SCREEN_HEIGHT - 40, 0, 0 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_CALCRECT, 0xFFFFAA00 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_NOCLIP, 0xFFFFAA00 );
sprintf( szStats, "Press G to pump out a screenshot" );
SetRect( &rc, 350, SCREEN_HEIGHT - 20, 0, 0 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_CALCRECT, 0xFFFFAA00 );
g_pFont->DrawText( NULL, szStats, -1, &rc, DT_NOCLIP, 0xFFFFAA00 );
}
// End rendering the scene and present it
g_pd3dDevice->EndScene();
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
return S_OK;
}
//-----------------------------------------------------------------------------
// Name: ScreenGrab()
// Desc: Saves the backbuffer to a file
//-----------------------------------------------------------------------------
//屏幕抓图小程序,没什么用
HRESULT ScreenGrab()
{
//
// Grab the back buffer and save it to a file
//
D3DDISPLAYMODE d3ddm;
g_pd3dDevice->GetDisplayMode( 0, &d3ddm );
g_pd3dDevice->CreateOffscreenPlainSurface( d3ddm.Width, d3ddm.Height, D3DFMT_A8R8G8B8,
D3DPOOL_DEFAULT, &g_pScreenshot, NULL );
g_pd3dDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &g_pScreenshot );
TCHAR szScreenshot[32];
sprintf( szScreenshot, "ScreenShot%d.bmp", g_uNumScreenshots++ );
D3DXSaveSurfaceToFile( szScreenshot, D3DXIFF_BMP, g_pScreenshot, NULL, NULL );
SAFE_RELEASE( g_pScreenshot );
return S_OK;
}
//OK 程序完了下面是fx程序
/*-----------------------------------------------------------------------------
Name : Soft Shadows.fx
Desc : Soft shadows effect file.
Author : Anirudh S Shastry. Copyright (c) 2004.
Date : 22nd June, 2004.
-----------------------------------------------------------------------------*/
#define FRAMESCALE 1.1f //加一个阴影图缩放参数
//--------------------------------------------
// Global variables
//--------------------------------------------
matrix g_matWorldViewProj : WorldViewProjection;
matrix g_matLightViewProj : LightViewProjection;
matrix g_matWorld : World;
matrix g_matWorldIT : WorldInverseTranspose;
matrix g_matTexture : Texture;
vector g_vLightPos : LightPosition;
vector g_vEyePos : EyePosition;
vector g_vLightColor : LightColor;
float2 g_vSampleOffsets[15];
float g_fSampleWeights[15];
texture tShadowMap;
texture tScreenMap;
texture tBlurHMap;
texture tBlurVMap;
texture tColorMap;
texture tSpotMap;
sampler ShadowSampler = sampler_state
{
texture = (tShadowMap);
MipFilter = Linear;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler ScreenSampler = sampler_state
{
texture = (tScreenMap);
MipFilter = Linear;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler BlurHSampler = sampler_state
{
texture = (tBlurHMap);
MipFilter = Linear;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler BlurVSampler = sampler_state
{
texture = (tBlurVMap);
MipFilter = Linear;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
sampler ColorSampler = sampler_state
{
texture = (tColorMap);
MipFilter = Linear;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Wrap;
AddressV = Wrap;
};
sampler SpotSampler = sampler_state
{
texture = (tSpotMap);
MipFilter = Linear;
MinFilter = Linear;
MagFilter = Linear;
AddressU = Clamp;
AddressV = Clamp;
};
//--------------------------------------------
// Vertex shaders
//--------------------------------------------
struct VSOUTPUT_SHADOW
{
float4 vPosition : POSITION;
float fDepth : TEXCOORD0;
};
// Shadow generation vertex shader
//发生阴影图的VS
VSOUTPUT_SHADOW VS_Shadow( float4 inPosition : POSITION )
{
// Output struct
VSOUTPUT_SHADOW OUT = (VSOUTPUT_SHADOW)0;
// Output the transformed position
OUT.vPosition = mul( inPosition, g_matLightViewProj );
// Output the scene depth
OUT.fDepth = OUT.vPosition.z;
return OUT;
}
// Shadow mapping vertex shader
struct VSOUTPUT_UNLIT
{
float4 vPosition : POSITION;
float4 vTexCoord : TEXCOORD0;
};
//绘制场景,只产生阴影图的VS
VSOUTPUT_UNLIT VS_Unlit( float4 inPosition : POSITION )
{
// Output struct
VSOUTPUT_UNLIT OUT = (VSOUTPUT_UNLIT)0;
// Output the transformed position
OUT.vPosition = mul( inPosition, g_matWorldViewProj );
OUT.vPosition.w *=FRAMESCALE; //将图像略缩小,多渲染一点边缘,以防止边缘部分模糊找不到数据
// Output the projective texture coordinates
OUT.vTexCoord = mul( inPosition, g_matTexture );
return OUT;
}
struct VSOUTPUT_BLUR
{
float4 vPosition : POSITION;
float2 vTexCoord : TEXCOORD0;
};
//模糊处理的VS,横向,纵向都用这一个
// Gaussian filter vertex shader
VSOUTPUT_BLUR VS_Blur( float4 inPosition : POSITION, float2 inTexCoord : TEXCOORD0 )
{
// Output struct
VSOUTPUT_BLUR OUT = (VSOUTPUT_BLUR)0;
// Output the position
OUT.vPosition = inPosition;
// Output the texture coordinates
OUT.vTexCoord = inTexCoord;
return OUT;
}
struct VSOUTPUT_SCENE
{
float4 vPosition : POSITION;
float2 vTexCoord : TEXCOORD0;
float4 vProjCoord : TEXCOORD1;
float4 vScreenCoord : TEXCOORD2;
float3 vNormal : TEXCOORD3;
float3 vLightVec : TEXCOORD4;
float3 vEyeVec : TEXCOORD5;
};
// Scene pixel shader
//最后渲染场景,贴阴影图
VSOUTPUT_SCENE VS_Scene( float4 inPosition : POSITION, float3 inNormal : NORMAL, float2 inTexCoord : TEXCOORD0 )
{
VSOUTPUT_SCENE OUT = (VSOUTPUT_SCENE)0;
// inPosition.w=1.5; //w参数是一个放大倍数
// Output the transformed position
OUT.vPosition = mul( inPosition, g_matWorldViewProj ); //变成屏幕空间坐标
// Output the texture coordinates
OUT.vTexCoord = inTexCoord;
// Output the projective texture coordinates
OUT.vProjCoord = mul( inPosition, g_matTexture );
//屏幕坐标计算
// Output the screen-space texture coordinates, //贴图坐标,这里的屏幕坐标为-1,1;-1,1
// OUT.vScreenCoord.x = ( OUT.vPosition.x * 0.5 + OUT.vPosition.w * 0.5 ); //转换为0-1
// OUT.vScreenCoord.y = ( OUT.vPosition.w * 0.5 - OUT.vPosition.y * 0.5 ); //
// OUT.vScreenCoord.z = OUT.vPosition.w;
// OUT.vScreenCoord.w = OUT.vPosition.w; //应该是线性放大
//w是缩放比例,这样,转换为屏幕贴图坐标
//缩小查找区域,与阴影图对上
OUT.vScreenCoord.x = ( (OUT.vPosition.x/FRAMESCALE)/OUT.vPosition.w + 1.f)/2.f; //转换为0-1
OUT.vScreenCoord.y = ( -(OUT.vPosition.y/FRAMESCALE)/OUT.vPosition.w + 1.f)/2.f; //
// Get the world space vertex position
float4 vWorldPos = mul( inPosition, g_matWorld );
// Output the world space normal
OUT.vNormal = mul( inNormal, g_matWorldIT );
// Move the light vector into tangent space
OUT.vLightVec = g_vLightPos.xyz - vWorldPos.xyz;
// Move the eye vector into tangent space
OUT.vEyeVec = g_vEyePos.xyz - vWorldPos.xyz;
return OUT;
}
//--------------------------------------------
// Pixel shaders
//--------------------------------------------
//发生阴影图的PS,这里把深度保存在RGB中了,不象别的直接存在Z-Buffer中
float4 PS_Shadow( VSOUTPUT_SHADOW IN ) : COLOR0
{
// Output the scene depth
return float4( IN.fDepth, IN.fDepth, IN.fDepth, 1.0f );
}
// Shadow mapping pixel shader
//绘制场景,只产生阴影图的PS
float4 PS_Unlit( VSOUTPUT_UNLIT IN ) : COLOR0
{
// Generate the 9 texture co-ordinates for a 3x3 PCF kernel
float4 vTexCoords[9];
// Texel size
float fTexelSize = 1.0f / 512.0f;
// Generate the tecture co-ordinates for the specified depth-map size
// 4 3 5
// 1 0 2
// 7 6 8
vTexCoords[0] = IN.vTexCoord;
/* vTexCoords[1] = IN.vTexCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.f );
vTexCoords[2] = IN.vTexCoord + float4( fTexelSize, 0.0f, 0.0f, 0.0f );
vTexCoords[3] = IN.vTexCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
vTexCoords[6] = IN.vTexCoord + float4( 0.0f, fTexelSize, 0.0f, 0.0f );
vTexCoords[4] = IN.vTexCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
vTexCoords[5] = IN.vTexCoord + float4( fTexelSize, -fTexelSize, 0.0f, 0.0f );
vTexCoords[7] = IN.vTexCoord + float4( -fTexelSize, fTexelSize, 0.0f, 0.0f );
vTexCoords[8] = IN.vTexCoord + float4( fTexelSize, fTexelSize, 0.0f, 0.0f );
*/
// Sample each of them checking whether the pixel under test is shadowed or not
float fShadowTerm = 0.0f;
for( int i = 0; i < 1; i++ ) //原程序使用了9点平均出深度值,考虑后面有模糊处理,这里没必要平均了,改为1点以提高速度
{
float A = tex2Dproj( ShadowSampler, vTexCoords[i] ).r;
float B = (IN.vTexCoord.z - 0.001f);
// Texel is shadowed
fShadowTerm += A < B ? 0.1f : 1.0f;
}
// Get the average
// fShadowTerm /= 9.0f;
// Uncomment this to disable 3x3 PCF
// float fShadowTerm = tex2Dproj( ShadowSampler, IN.vTexCoord ) < (IN.vTexCoord.z - 0.001f) ? 0.1f : 1.0f;
return fShadowTerm;
}
// Horizontal blur pixel shader
//水平模糊处理
float4 PS_BlurH( VSOUTPUT_BLUR IN ) : COLOR0
{
// Accumulated color
float4 vAccum = float4( 0.0f, 0.0f, 0.0f, 0.0f );
// Sample the taps (g_vSampleOffsets holds the texel offsets and g_fSampleWeights holds the texel weights)
for( int i = 0; i < 8; i++ ) //原文为15个点,缩小一点提高速度
{
vAccum += tex2D( ScreenSampler, IN.vTexCoord + g_vSampleOffsets[i] ) * g_fSampleWeights[i];
}
return vAccum;
}
// Vertical blur pixel shader
//纵向模糊处理,这样分两次模糊了64个点,实际取样为8+8次,比8*8次大为减少,这是本程序的要点
float4 PS_BlurV( VSOUTPUT_BLUR IN ) : COLOR0
{
// Accumulated color
float4 vAccum = float4( 0.0f, 0.0f, 0.0f, 0.0f );
// Sample the taps (g_vSampleOffsets holds the texel offsets and g_fSampleWeights holds the texel weights)
for( int i = 0; i < 8; i++ )
{
vAccum += tex2D( BlurHSampler, IN.vTexCoord + g_vSampleOffsets[i] ) * g_fSampleWeights[i];
}
return vAccum;
}
//绘制场景贴阴影
float4 PS_Scene( VSOUTPUT_SCENE IN ) : COLOR0
{
// Normalize the normal, light and eye vectors
IN.vNormal = normalize( IN.vNormal );
IN.vLightVec = normalize( IN.vLightVec );
IN.vEyeVec = normalize( IN.vEyeVec );
// Sample the color and normal maps
float4 vColor = tex2D( ColorSampler, IN.vTexCoord );
// Compute the diffuse and specular lighting terms
float diffuse = max( dot( IN.vNormal, IN.vLightVec ), 0 );
float specular = pow( max( dot( 2 * dot( IN.vNormal, IN.vLightVec ) * IN.vNormal - IN.vLightVec, IN.vEyeVec ), 0 ), 8 );
if( diffuse == 0 ) specular = 0;
// Grab the shadow term
//取出阴影图像素
// float fShadowTerm = tex2Dproj( BlurVSampler, IN.vScreenCoord );
float fShadowTerm = tex2D( BlurVSampler, IN.vScreenCoord.xy ); //原程序使用tex2Dproj,改为tex2D好理解,当然,前面坐标计算也改了
// float fShadowTerm = tex2Dproj( ScreenSampler, IN.vScreenCoord );
// Grab the spot term
float fSpotTerm = tex2Dproj( SpotSampler, IN.vProjCoord );
// Compute the final color
return (diffuse * vColor * g_vLightColor * fShadowTerm * fSpotTerm) + //合成
(specular * vColor * g_vLightColor.a * fShadowTerm * fSpotTerm);
// return float4(fShadowTerm,fShadowTerm ,fShadowTerm ,1);
/* return (diffuse * vColor * g_vLightColor * fSpotTerm) +
(specular * vColor * g_vLightColor.a * fSpotTerm);*/
}
//不加模糊直接产生硬阴影,没用
float4 PS_SceneHard( VSOUTPUT_SCENE IN ) : COLOR0
{
// Normalize the normal, light and eye vectors
IN.vNormal = normalize( IN.vNormal );
IN.vLightVec = normalize( IN.vLightVec );
IN.vEyeVec = normalize( IN.vEyeVec );
// Sample the color and normal maps
float4 vColor = tex2D( ColorSampler, IN.vTexCoord );
// Compute the ambient, diffuse and specular lighting terms
float diffuse = max( dot( IN.vNormal, IN.vLightVec ), 0 );
float specular = pow( max( dot( 2 * dot( IN.vNormal, IN.vLightVec ) * IN.vNormal - IN.vLightVec, IN.vEyeVec ), 0 ), 8 );
if( diffuse == 0 ) specular = 0;
// Grab the spot term
float fSpotTerm = tex2Dproj( SpotSampler, IN.vProjCoord );
// Grab the shadow term
float fShadowTerm = 0.0f;
fShadowTerm = tex2Dproj( ShadowSampler, IN.vProjCoord ) < (IN.vProjCoord.z - 0.001f) ? 0.1f : 1.0f;
fShadowTerm *= fSpotTerm;
/* // Generate the 9 texture co-ordinates for a 3x3 PCF kernel
float4 vTexCoords[9];
// Texel size
float fTexelSize = 1.0f / 512.0f;
// Generate the tecture co-ordinates for the specified depth-map size
// 4 3 5
// 1 0 2
// 7 6 8
vTexCoords[0] = IN.vProjCoord;
vTexCoords[1] = IN.vProjCoord + float4( -fTexelSize, 0.0f, 0.0f, 0.0f );
vTexCoords[2] = IN.vProjCoord + float4( fTexelSize, 0.0f, 0.0f, 0.0f );
vTexCoords[3] = IN.vProjCoord + float4( 0.0f, -fTexelSize, 0.0f, 0.0f );
vTexCoords[6] = IN.vProjCoord + float4( 0.0f, fTexelSize, 0.0f, 0.0f );
vTexCoords[4] = IN.vProjCoord + float4( -fTexelSize, -fTexelSize, 0.0f, 0.0f );
vTexCoords[5] = IN.vProjCoord + float4( fTexelSize, -fTexelSize, 0.0f, 0.0f );
vTexCoords[7] = IN.vProjCoord + float4( -fTexelSize, fTexelSize, 0.0f, 0.0f );
vTexCoords[8] = IN.vProjCoord + float4( fTexelSize, fTexelSize, 0.0f, 0.0f );
// Sample each of them checking whether the pixel under test is shadowed or not
for( int i = 0; i < 9; i++ )
{
float A = tex2Dproj( ShadowSampler, vTexCoords[i] ).r;
float B = IN.vProjCoord.z - 0.001f;
// Texel is shadowed
fShadowTerm += A < B ? 0.1f : 1.0f;
}
// Get the average
fShadowTerm = fShadowTerm * fSpotTerm / 9.0f;
*/
// Compute the final color
return (diffuse * vColor * g_vLightColor * fShadowTerm) +
(specular * vColor * g_vLightColor.a * fShadowTerm);
}
//--------------------------------------------
// Techniques
//--------------------------------------------
technique techShadow
{
pass p0
{
Lighting = False;
CullMode = CCW;
VertexShader = compile vs_2_0 VS_Shadow();
PixelShader = compile ps_2_0 PS_Shadow();
}
}
technique techUnlit
{
pass p0
{
Lighting = False;
CullMode = CCW;
VertexShader = compile vs_2_0 VS_Unlit();
PixelShader = compile ps_2_0 PS_Unlit();
}
}
technique techBlurH
{
pass p0
{
Lighting = False;
CullMode = None;
VertexShader = compil vs_2_0 VS_Blur();
PixelShader = compile ps_2_0 PS_BlurH();
}
}
technique techBlurV
{
pass p0
{
Lighting = False;
CullMode = None;
VertexShader = compile vs_2_0 VS_Blur();
PixelShader = compile ps_2_0 PS_BlurV();
}
}
technique techScene
{
pass p0
{
Lighting = False;
CullMode = CCW;
VertexShader = compile vs_2_0 VS_Scene();
PixelShader = compile ps_2_0 PS_Scene();
}
}
technique techSceneHard
{
pass p0
{
Lighting = False;
CullMode = CCW;
VertexShader = compile vs_2_0 VS_Scene();
PixelShader = compile ps_2_0 PS_SceneHard();
}
}
//Fx完了
//笨笨 2006.02.12