個人檔案飘零风雨亭部落格清單 工具 說明

部落格


3月14日

Symbol Paths(转)

Symbol Paths(转)
http://msdn2.microsoft.com/en-us/library/ms680689(VS.85).aspx
The library uses the symbol search path to locate debug symbols (.dbg file) for .dll, .exe, and .sys files by appending "\symbols" and "\dll" or "\exe" or "\sys" to the path. For example, the typical location of symbol files for .dll files is c:\mysymbols\symbols\dll. For .exe files, the location is c:\mysymbols\symbols\exe.
To specify where the symbol handler will search disk directories for symbol files, call the SymSetSearchPath function. Alternatively, you can specify a symbol search path in the UserSearchPath parameter of the SymInitialize function.
The UserSearchPath parameter in SymInitialize and the SearchPath parameter in SymSetSearchPath take a pointer to a null-terminated string that specifies a path, or series of paths separated by a semicolon. The symbol handler uses these paths to search for symbol files. If this parameter is specified as a non-null value, the symbol handler searches only the paths set by the application. If this parameter is NULL, the symbol handler first searches the current working directory of the application, then the system root directory (%windir%). If you set the _NT_SYMBOL_PATH or _NT_ALT_SYMBOL_PATH environment variable, the symbol handler searches for symbol files in the following order:
The current working directory of the application.
The _NT_SYMBOL_PATH environment variable.
The _NT_ALT_SYMBOL_PATH environment variable.
To retrieve the search paths, call the SymGetSearchPath function.
The search path for program database (.pdb) files is different than the path for debug (.dbg) files. The algorithm is determined by the functionality of the symbol library. By default, Microsoft Visual C/C++ creates Microsoft format symbols, strips them from the image, and places them in a separate .pdb file. Typically, the .pdb file will be located in the directory that contains the executable image. Visual C/C++ embeds the absolute path to the .pdb file in the executable image. If the symbol handler cannot find the .pdb file in that location or if the .pdb file was moved to another directory, the symbol handler will locate the .pdb file using the search path described for .dbg files.

如何获取windows的调试符号文件 (转)

如何获取windows的调试符号文件 (转)
http://blog.vckbase.com/zyq654321/archive/2004/11/01/1188.aspx

       PDB(Program Database) file是一种最新的用于存贮debug信息的格式文件。

与它的两个“前辈”(COFF和CODEVIEW)不同的是,PDB可以保存所有种类的debug信息,并支持增量链接(incremental link)。此外,PDB文件是以单独文件的形式出现,而COFF和COEDEVIEW两种格式的debug信息是存在执行文件内的。(注:CODEVIEW格式的debug信息也可存于执行文件名,其后缀名为.dbg).

通常在调试时,我们无法获得一些系统DLL的调试符号,这样会让我们对程序运行的细节的了解可能会有一些问题,当然我们可以通过查看反汇编窗口来解决一些问题。但是如果你能够得到这个调试符号,是不是觉得会很爽。呵呵,爽是次要的,重要的是这些调试符号会强化你所需要的线索。要实现这一点,就须要有相应的windows调试符号文件,你可以通过下面的方法获得它:

symsrv*symsrv.dll*f:\localsymbols*http://msdl.microsoft.com/download/symbols

将此句加在项目属性中的[符号路径处](VC++.NET)即可。 其中f:\localsymbols是你的本地盘路径,请先确保它的存在。完成后,F5进行debug。第一次是时间比较长,这是因为在下载符号库文件。你可以在output窗口中看到一个接一个的符号库被加载……

提示,设定了此属性后可能会导致debug的速度下降。当然这是正常现象,东东多了么。

现在就去下它们吧,Enjoy!

 

另外也可以通过设定环境变量来实现:

环境变量法:
Set _NT_SYMBOL_PATH = symsrv*symsrv.dll*f:\localsymbols*http://msdl.microsoft.com/download/symbols

具体设定位置:My Computer--->Properties---->Advanced--->Environment Varibles

不过偶建议最好用时再设它,不用时就不要设它,免得影响速度。


 

注: 关于获取SymSrv.dll ,请安装 Debugging Tools for Windows package
 

1月28日

博客迁移

由于spaces.live的博客用起来不方便也不美观
现已迁移到新的地址:
 
不过这里可能继续会发些零碎笔记
1月14日

lua写文件(备忘)

lua写文件(备忘):
 
function save(n)
 io.output("save"..n)
 serialize(contex)
 io.close()
end
-- from the lua tutorials
function serialize (o)
    if type(o) == "number" then
    io.write(o)
    elseif type(o) == "string" then
    io.write(string.format("%q", o))
    elseif type(o) == "table" then
    io.write("{\n")
    for k,v in pairs(o) do
        io.write("  [")
        serialize(k)
        io.write("] = ")
        serialize(v)
        io.write(",\n")
    end
    io.write("}\n")
    else
    error("cannot serialize a " .. type(o))
    end
end
--------------------------------------------

msn登录后自动关闭问题解决

关键字: msn自动关闭
msn登录后自动关闭问题解决
我搜索了发现 这个目前网上还没正确答案!
先说说经历,可以直接跳到后面看解决办法!
 
一.经历:
前两天发现msn成功登录后, 鼠标移到上面就自动关闭了
不能进行任何操作,而微软的ie打开链接经常莫名奇秒地自动关闭
然后我用"世界之窗"(theworld.exe)一般没问题,就将就用了.
今天想用msn,却用不上,气了. 
把以前那个反安装,然后重下个msn8.1安装后还是一样!
装了几个杀毒软件和木马工具
狂杀一遍,然后重起,不行!
看来得手动解决,首先用procexp.exe查看当前进程,没发现可疑的,
再用autoruns.exe查看启动项没有什么问题,查看它的服务项,好象
也没什么问题,除了几个我知道的软件外都标明软件公司的产品(一般的木马都不标微软的产品用来骗新手,
我初时也没在意,病毒其实就在里面).后面用360安全卫士查了一软件,并装上所有系统补丁(360会提示没装的系统补丁,我喜欢这个)
然后全面查找一遍,看看系统服务有个不打问题的ntmlsvc.exe上面标明是微软的产品...我好象没见过这个东西.于是点中它后点击修复选中
项,然后再上msn,晕还是不行,再刷新一遍那个ntmlsvc.exe怎么还在?这个修复选中项好象没用啊?
我于是到高级里的系统服务状态看看ntmlsvc.exe确实被启动的,右击它选停止,然后再查,没有了.
这时打开msn就可以正常上了!
 
二.解决办法:
其实很简单,用系统带的系统服务(在"开始-设置-控制面板-管理工具-服务"里),
查看到 ntmlsvc 服务,右击它把它停止就可以了.
或者用360safe.exe中的高级-系统状态-右击它停止也可以!
1月3日

fatal error C1900(备忘)

fatal error C1900:
1>------ Build started: Project: sanguo, Configuration: Release Win32 ------
1>Linking...
1>fatal error C1900: Il mismatch between 'P1' version '20060201' and 'P2' version '20050411'
1>LINK : fatal error LNK1257: code generation failed
1>Build log was saved at "file://e:\project\sanguo\temp\release\BuildLog.htm"
1>sanguo - 1 error(s), 0 warning(s)

上网查了一下估计是链接库打了补丁,而我的没打,造成vc编译版本不一致
 
to fan:
i can't email to you because your setting's limit.
fatal error C1900 generate because your compile tools's version different from the lib's(which someone compiled it using another vc2005).
solve this problem, only need patch the same sp1 or sp2 for your vc2005(i.e. your program and the lib complier's patch must same)!
 
12月20日

关于keystate(备忘)

1. GetKeyState(VK_CONTROL) & 0x80
 2. GetAsyncKeyState(VK_LSHIFT) & 0x8000
3. BOOL GetKeyboardState(PBYTE lpKeyState);
   if(keystate[VK_RSHIFT])
   {
      // +++
   }
区别:
1:从windows消息队列中取得键盘消息,返回key status.
2:直接侦测键盘的硬件中断,返回key status.
3:当从windows消息队列中移除键盘消息时,才返回key status.

new抛出异常的一种情况

最近修改玩家反馈的bug(dmp文件)

有些内存错误很难查。

在一次无意中发现,在new中抛出异常的一种情况,
就是如果前面先new出一个内存,然后你对此内存越界修改(改到后面去了)
之后你再new新内存时就可能抛出异常;
例如你new char p[100];
然后你修改 for( int =0; i<200); ++i){ p[i]=数据; }
完成后再new Mydata[10];
就可能抛出异常!

以后crash发现new异常时可查看是否上述情况:)

maxsdk图解

参见:
3DSMAX_SDK_DavidLanier.pdf
max_pipeline(instance)max_pipeline(refrence)max_pipelineNodeTM
12月12日

lua这篇文章不错

在google搜索一下Lua源码分析
可以找到,刚好以前看看过lua源码,这个就看懂了
 
o_lua.architecture3o_lua.object

程序与lua脚本数据交换(备忘)

(这些是以前跟踪分析的,其实挺简单,只是不懂时用来跟踪一遍就完全明白了, 写在这里是为了备忘)
先给一下lua的值定义:
lua中的值:
TValue{
union{gc*,void*,int,bool}; //可回收对象,userlightdata, number, bool
tt; //类别
};
其中gc结构如下:
GC{
gch; // gc head
ts;   //  tstring
u;    // user data
cl;   // closure
h;   // table
p;   // proto 
uv; // upval
th;  // theard
};
 
一。脚本给程序传表格
以下代码摘自boswars,用来分析:
脚本如下:
test( {Name = "sprite-mana", File = "general/mana2.png", Offset = {0, -1}, Size = {31, 4}} ) // {}中即是表格
程序如下
void test(lua_State *l)
{
  lua_pushnil(l);
  while (lua_next(l, i + 1)) {
   key = LuaToString(l, -2); // key name
   if (!strcmp(key, "Name")) {
    name = LuaToString(l, -1);
   }
   else if (!strcmp(key, "File")) {
    File = LuaToString(l, -1);
   }
   else if (!strcmp(key, "Offset")) {
    if (!lua_istable(l, -1) || luaL_getn(l, -1) != 2) {
     LuaError(l, "incorrect argument");
    }
    lua_rawgeti(l, -1, 1); // offsetX
    lua_rawgeti(l, -2, 2); // offsetY
    HotX = LuaToNumber(l, -2);
    HotY = LuaToNumber(l, -1);
    lua_pop(l, 2); // Pop offsetX and Y
   }
   else if (!strcmp(key, "Size")) {
    if (!lua_istable(l, -1) || luaL_getn(l, -1) != 2) {
     LuaError(l, "incorrect argument");
    }
    lua_rawgeti(l, -1, 1); // Width
    lua_rawgeti(l, -2, 2); // Height
    Width = LuaToNumber(l, -2);
    Height = LuaToNumber(l, -1);
    lua_pop(l, 2); // Pop Width and Height
   }
   else { // Error.
    LuaError(l, "incorrect field '%s' for the DefineSprite." _C_ key);
   }
   lua_pop(l, 1); // pop the value;
  }
}
lua调用我们的函数void test(lua_State *l)后变成:
--- 表
我们需要先
lua_pushnil(l);(把nil作为key)后:
--- nil
--- 表
lua_next(l,1)后://1是表的位置,key则是上面的nil
--- value // 得到的value
--- key   // 得到的key
--- 表
lua_rawgeti(l, -1, 1); //offsetX; 参数-1是table(value是一个表),
                       // 1是表的下标, 得到offsetX存在top,并inc top
--- offsetX
--- value
--- key
--- 表
lua_rawgeti(l, -2, 1); //offsetY; 参数-2是table(value是一个表)(因为上面压入offsetX,所以变成-2了)
                       // 1是表的下标, 得到offsetX存在top,并inc top
--- offsetY
--- offsetX
--- value
--- key
--- 表
使用
HotX = LuaToNumber(l, -2);
HotY = LuaToNumber(l, -1);
保存得到的offsetX和offsetY
然后将它们出栈:
lua_pop(l, 2); // Pop offsetX and offsetY
变成:
--- value
--- key
--- 表
再将value出栈:
lua_pop(l, 1); // pop the value;
变成:
--- key
--- 表
最后回到while函数继续调用
lua_next(l,1)://1是表的位置,key则是上面的key(上一次得到的key),
              //这样些函数数又将会得到下一个key和value放堆栈中
变成:
--- 下一个value
--- 下一个key
--- 表             
如此循环!
 
附:
这里有一篇类似文章参见:
使用lua_next()遍历表
 
 
二。程序生成表格给脚本:
以下代码也是从boswar拷过来的,挺简单就不分析了
static int CclFilteredListDirectory(lua_State *l, int type, int mask)
{
 char directory[256];
 const char *userdir;
 std::vector<FileList> flp;
 int n;
 int i;
 int j;
 int pathtype;
 LuaCheckArgs(l, 1);
 userdir = lua_tostring(l, 1);
 n = strlen(userdir);
 pathtype = 0; // path relative to stratagus dir
 if (n > 0 && *userdir == '~') {
  // path relative to user preferences directory
  pathtype = 1;
 }
 // security: disallow all special characters
 if (strpbrk(userdir, ":*?\"<>|") != 0 || strstr(userdir, "..") != 0) {
  LuaError(l, "Forbidden directory");
 }
 if (pathtype == 1) {
  ++userdir;
  sprintf(directory, "%s/%s", UserDirectory.c_str(), userdir);
 } else {
  sprintf(directory, "%s/%s", StratagusLibPath.c_str(), userdir);
 }
 lua_pop(l, 1); 
 lua_newtable(l);
 n = ReadDataDirectory(directory, NULL, flp);
 for (i = 0, j = 0; i < n; ++i) {
  if ((flp[i].type & mask) == type) {
   lua_pushnumber(l, j + 1);
   lua_pushstring(l, flp[i].name);
   lua_settable(l, 1);
   ++j;
  }
  delete[] flp[i].name;
 }
 return 1;
}
 
附:这个可参考:
Lua 语言和C集成调研小结
11月20日

游戏中换装处理(备忘)

游戏中换装处理(备忘)
一.cha 结构:
cha中可有几个model(对应为部位,可切换)
每个model可有几个mtl(即subset), 
每个mtl可作为一个pass, 该mtl中可有几层贴图
但是由于硬件多层贴图的限制可考虑只充许2层, 否则可能需要多次pass
二.cha update:
updatebone(计算当前骨骼运行时矩阵) -- 注意,骨骼的骨架数据及骨骼的动画数据可共享
updatemodel(计算模型中动画)        -- 注意,模型的网格数据及模型的动画数据可共享
注意:
  需要将每model中引用到的骨骼运行时矩阵(在updatebone中计算好了)拷贝到该model中,
  以便在后面渲染每个模型时能用上.
 
优化:
    人物不可见时可不更新动画,但要更新动作(即当前播放第几个动作的第几帧),
故上述两函数可再追加一个标志说明是更新动作还是动画
三.cha render:
遍历渲染每一个model:
for(i loop models)
{
 1. 设置每个模型的vb和ib: (vb和ib是静态创建的)
    setstream(model's vb && ib)
 
 2. 渲染每一个模型
   a. 把model引用到的运行时矩阵乘上MVP设置给vs
   b. 分pass画:
  for(i loop mtls){
     把每个mtl设置给vs
     drawpass(i)
  }
}
优化:
   在第2部时要把model引用到的运行时矩阵乘上MVP设置给vs中的常量寄存器,
然后用每个顶点的矩阵索引及权重进行混合(矩阵索引需要vs1.1中的寻址寄存器a0.x)
另外,只有整体transform变换的模型(没有顶点或骨骼动画, 如地面上不同位置不同面向的不动的树)要与人物模型分开处理,
这类物件可用类似上述的常量矩阵数组的技术进行instancing批次渲染(带skin的instancing批次渲染由于常量寄存器已变骨骼矩阵使用,
因此一般需要硬件支持,如使用顶点纹理来保存骨骼矩阵,参见前面文章instancing批次渲染)

疑问:
目前有一个疑问,上述的渲染需不需要换成按贴图排序再渲染.
还有怎么在只支持vs1.1的显卡下实现skin instancing,或者有其它什么优化的方案?
11月19日

instancing批次画

instancing批次画:
具体实现参见dxsdk9.0c中的instancing_2005(未包括最后一种需要sm3.0支持的技术)
一.单个shade const:
    V( g_pEffect->Begin( &cPasses, 0 ) );
    for( iPass = 0; iPass < cPasses; iPass++ )
    {
        V( g_pEffect->BeginPass( iPass ) );
           
        // Render the boxes with the applied technique
        V( g_pEffect->SetTexture( g_HandleTexture, g_pBoxTexture ) );
        for( int nRemainingBoxes = 0; nRemainingBoxes < g_NumBoxes; nRemainingBoxes++ )
        {
            // set the box instancing array
            V( g_pEffect->SetVector( g_HandleBoxInstance_Position, &g_vBoxInstance_Position[nRemainingBoxes] ) );
            V( g_pEffect->SetVector( g_HandleBoxInstance_Color, (D3DXVECTOR4*)&g_vBoxInstance_Color[nRemainingBoxes] ) );
           
            // The effect interface queues up the changes and performs them
            // with the CommitChanges call. You do not need to call CommitChanges if
            // you are not setting any parameters between the BeginPass and EndPass.
            V( g_pEffect->CommitChanges() );
           
            V( pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, 4 * 6, 0, 6 * 2 ) );
        }
        V( g_pEffect->EndPass() );
    }
    V( g_pEffect->End() );
二.多个shade const(因为使用类似于矩阵板实现skin bone的方法,所以存在冲突不能同时使用在骨骼动画中)
    V( g_pEffect->Begin( &cPasses, 0 ) );
    for( iPass = 0; iPass < cPasses; iPass++ )
    {
        V( g_pEffect->BeginPass( iPass ) );
           
        // Render the boxes with the applied technique
        V( g_pEffect->SetTexture( g_HandleTexture, g_pBoxTexture ) );
        int nRemainingBoxes = g_NumBoxes;
        while( nRemainingBoxes > 0 )
        {
            // determine how many instances are in this batch (up to g_nNumBatchInstance)          
            int nRenderBoxes = min( nRemainingBoxes, g_nNumBatchInstance );
            // set the box instancing array
            V( g_pEffect->SetVectorArray( g_HandleBoxInstance_Position, g_vBoxInstance_Position + g_NumBoxes - nRemainingBoxes, nRenderBoxes) );
            V( g_pEffect->SetVectorArray( g_HandleBoxInstance_Color, (D3DXVECTOR4*)(g_vBoxInstance_Color + g_NumBoxes - nRemainingBoxes), nRenderBoxes) );
           
            // The effect interface queues up the changes and performs them
            // with the CommitChanges call. You do not need to call CommitChanges if
            // you are not setting any parameters between the BeginPass and EndPass.
            V( g_pEffect->CommitChanges() );
           
            V( pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, nRenderBoxes * 4 * 6, 0, nRenderBoxes * 6 * 2 ) );
            // subtract the rendered boxes from the remaining boxes
            nRemainingBoxes -= nRenderBoxes;
        }
        V( g_pEffect->EndPass() );
    }
    V( g_pEffect->End() );

三. vs3.0 的hwinstance(需要多流INSTANCEDATA支持):
   这种方法是GPU通过虚拟复制(virtually duplicating)把顶点从第一个流打包到第二个流中。(也就是硬件使用多流和自动打包的方式帮忙实际了类似上述第二种的instance批次画)
 
    // Stream zero is our model, and its frequency is how we communicate the number of instances required,
    // which in this case is the total number of boxes
    V( pd3dDevice->SetStreamSource( 0, g_pVBBox, 0, sizeof(BOX_VERTEX)) );
    V( pd3dDevice->SetStreamSourceFreq( 0, D3DSTREAMSOURCE_INDEXEDDATA | g_NumBoxes ) );
       
    // Stream one is the instancing buffer, so this advances to the next value
    // after each box instance has been drawn, so the divider is 1.
    V( pd3dDevice->SetStreamSource( 1, g_pVBInstanceData, 0, sizeof( BOX_INSTANCEDATA_POS ) ) );
    V( pd3dDevice->SetStreamSourceFreq( 1, D3DSTREAMSOURCE_INSTANCEDATA | 1ul ) );
    V( pd3dDevice->SetIndices( g_pIBBox ) );
       
    // Render the scene with this technique
    // as defined in the .fx file
    V( g_pEffect->SetTechnique( g_HandleTechnique ) );
       
    V( g_pEffect->Begin( &cPasses, 0 ) );
    for( iPass = 0; iPass < cPasses; iPass++ )
    {
        V( g_pEffect->BeginPass( iPass ) );
           
        // Render the boxes with the applied technique
        V( g_pEffect->SetTexture( g_HandleTexture, g_pBoxTexture ) );
           
        // The effect interface queues up the changes and performs them
        // with the CommitChanges call. You do not need to call CommitChanges if
        // you are not setting any parameters between the BeginPass and EndPass.
        V( g_pEffect->CommitChanges() );
           
        V( pd3dDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST, 0, 0, 4 * 6, 0, 6 * 2 ) );
           
        V( g_pEffect->EndPass() );
    }
    V( g_pEffect->End() );
       
    V( pd3dDevice->SetStreamSourceFreq( 0, 1 ) );
    V( pd3dDevice->SetStreamSourceFreq( 1, 1 ) );

四. 带骨骼实现的批次(顶点纹理方式):
    上述方法不好同时实现skinning, 如果需要实现skinning,可以尝试把所有实体的所有骨骼信息储存为一张纹理,之后为相应的实体选择正确的骨骼,这需要用到Shader Model3.0中的顶点纹理访问功能。如果使用这种技术,那么访问顶点纹理带来的性能消耗是不确定的,应该实现进行测试。
 
 
具体例子也可以参考:
NoInstancing:
NoInstancing
ShaderInstancing:
ShaderInstancing
HardwareInstancing:
HardwareInstancing
VFetchInstancing:
VFetchInstancing
 
 
 
11月14日

WOW的地形shader祥细解释

WOW的地形shader祥细解释
SPXG // 标识
// 用到5张图片:
{
   blendTexture  = 0
   layer0Texture = 1
   layer1Texture = 2
   layer2Texture = 3
   layer3Texture = 4
}
!!ARBfp1.0
// 定义常量:
PARAM c[1] = { { 1, 0.30000001, 0.69999999 } };
// 声明3个寄存器:
TEMP R0;
TEMP R1;
TEMP R2;
// 开始混合:
// 一.第0层和第1层使用第4张图的x通道作为alpha进行混合:
TEX R1, fragment.texcoord[0], texture[0], 2D;
TEX R0, fragment.texcoord[1], texture[1], 2D;
ADD R2, R0, -R1;
// r2=Tex1-Tex0
TEX R0, fragment.texcoord[4], texture[4], 2D;
MAD R2, R0.x, R2, R1;
// r2 = Tex4.x*r2+Tex0
// 说明:
// 其中的Tex4.x是对应第1层alpha值, 下面把Tex4.x当a1看
// 即 r2 = a1 * (Tex1-Tex0) + Tex0
// 转换一下即是: Tex0*(1-a1)+a1*Tex1, 呵呵,看到了吧,这就是混合公式!
// 二.第2层和前面结果使用第4张图的y通道作为alpha进行混合:
TEX R1, fragment.texcoord[2], texture[2], 2D;
ADD R1, R1, -R2;
// r1=Tex2-r2
MAD R2, R0.y, R1, R2;
// r2 = Tex4.y*r1+r2, 即 a2*r1+r2 = a2*(Tex2-r2)+r2, 即r2*(1-a2)+Tex2*a2, 其中r2即是上次0和1层混合后的结果
// 三.第3层和前面结果使用第4张图的z通道作为alpha进行混合:
TEX R1, fragment.texcoord[3], texture[3], 2D;
ADD R1, R1, -R2;
// r1=Tex3-r2
MAD R1, R0.z, R1, R2;
// r1=a3*r1+r2 , 即 a3*r1+r2 = a3*(Tex3-r2)+r2, 即r2*(1-a3)+Tex3*a3
// 这样r1就保存了最终的混合结果
// 四.让阴影地表光泽系数为0(Tex4.w即a通道代表地形的阴影,0为阴影,1为正常.而每层贴图中的a通道是光泽通道,所以R1.w保存的是最终的光泽通道值):
MUL R0.x, R1.w, R0.w;
// r0.x = r1.w(光泽)*Tex4.w(阴影值, 是阴影则=0,否则=1)
// 计算削减系数?
MAD R0.w, R0, c[0].y, c[0].z; // r0乘上0.3(削减了30%)再加上一个常量0.69999999
// 反射高光 = secondary_color(反射光)*光泽:
MUL R0.xyz, R0.x, fragment.color.secondary;
// 贴图最终color = 最终贴图混出的color*削减系数:
MUL R1.xyz, R1, R0.w;
// 贴图最终color * primary_color(光照色或是顶点色) + 反射高光:
MAD result.color.xyz, R1, fragment.color.primary, R0;
// alpha:
MOV result.color.w, c[0].x;
END
  
  
  
参考资料:
<WOW的地形渲染>:
http://dev.csdn.net/author/ZERO2046/1b8df53b7f034e7999d70e0cac5665f6.html
魔兽世界的地形渲染,基本上有三种渲染路径:固定渲染管线(其中是不是又分几种就不清楚了);shader(带高光);shader(不带高光)
  用到shader的渲染路径又分别针对1层,2层,3层,4层(最多允许每个chunk使用4层纹理)专门写了shader代码。
  用MyWarCraftStudio打开WOW的misc.mpq包,shader \ pixel \ 目录下以"terrain"打头的bls文件就是地形渲染使用的shader,带有"_s"后缀的是带高光的渲染,否则就是不带高光的。
  我仔细看了其中的terrain4_s.bls(用UltraEdit之类的工具可以直接当成文本文件打开)。是汇编形式的ps代码,由于之前俺只用过c形式的HLSL所以看起来有点吃力,好在还是看明白了。
  texture0~3就是待混合的4层纹理,每层纹理的a通道是该层纹理对应的高光通道;texture4是一张用来控制混合权重的alpha纹理。texture4的r,g,b通道分别对应texture1~3的alpha值,而texture4.a则代表地形的阴影,0为阴影,1为正常。
  混合的公式为res_n = res_n-1 * ( 1 - alpha_n ) + texture_n * alpha_n。其中n代表第n层纹理。res_n代表第n层混合后的结果。
  从代码可以看出,最终混合结果的a通道(高光通道)被乘以阴影值,也就是说阴影中没有高光。同时,阴影中的diffuse光照被削减了30%。
  1,2,3层纹理混合,以及不带高光的情况想必也没有什么特别之处,也不去细看了。
  值得一提的是我原来一直以为WOW的地形渲染只用了ps_1_1,但从这个shader看用了5张纹理,超出了ps_1_1的4个纹理采样的限制,所以至少使用了ps_1_4(允许6个纹理采样)。而1~3层纹理混合的情况使用ps_1_1就够了。我不知道在渲染时频繁切换ps版本会不会收到性能上的惩罚,但有些显卡(比如NV的5200)同样的代码在ps_1_4上运行要比在ps_1_1上运行慢很多。
  至于固定渲染管线的渲染路径,由于不可能看到代码,具体如何实现多层纹理混合无法揣测,集合了各层纹理混合系数的alpha图应该不能直接用于渲染,因为固定渲染管线似乎只能用a通道来控制混合系数,可能需要将这张纹理拆成4张才行。。。

地形上网格投射阴影(备忘)

地形上网格投射阴影:
目前有很多游戏及引擎用到了这种方法,
如开源的glest即时战略游戏和OGRE3D,
方法是:
照相到对到光线方向,渲染地表到深度缓冲,再渲染要投影的物件到贴图
然后将照相机设到顶视图,设置投影映射纹理把贴图贴到地面.

具本参见:
Generating Mesh Shadows On Terrain Using OpenGL
http://gpwiki.org/index.php/Generating_Mesh_Shadows_On_Terrain_Using_OpenGL

地形pvs

地形pvs
将地形分成n*n个区域,分成32层
然后,遍历每个区域, 在每个区域上的每层上向其它区域拉一直接,测试它们之间是否有障碍(不用测试三角形只用比较经过的区域内每个格子高度),是则表示当前所在区域看不见所测区域,保存此PVS供用.
注意PVS要用bit来保存,否则很大

参见:
GameDev.net -- Terrain Geomorphing in the Vertex Shader
http://www.gamedev.net/columns/hardcore/geomorph/

geomorph可将格子进行4次lod, 并在不同lod的格子边界进行插入点形成新的三角形(以免出现T形裂缝)
由于lod形成的高度差,所以还可以设置一个error充许值.具体参见原文(该地形也实现了类似目前一些网游所用的alpha混合多层贴图)

地形碰撞高度计算(备忘)


地形碰撞计算:
一.如果是鼠标点击地面,
可将射线固定长度并分段,然后用射线与每一个分段上的点所落在的地面tile(两个三角形)进行求交,
相交则返回对应位置,否则继续与下一个段上的点所落在的地面tile求交
/*
      0      1
       ---->
      | \  |
      |  \ |
    2 V ---  3   
*/
这里不讨论点击屏幕求射线方法与射线分段处理,只算求交
如下d3d求交得出uv,再求位置:
 VECTOR3 vPickPos;
 // 右边:
 if( D3DXIntersectTri(&v0, &v1, &v3, &vOrig, &vDir, &u, &v, NULL) == TRUE)
 {
  vPickPos = v0 + u * (v1 - v0) + v * (v3 - v0); 
  return vPickPos.z;
 }
 // 左边:
 if( D3DXIntersectTri(&v0, &v3, &v2, &vOrig, &vDir, &u, &v, NULL) == TRUE)
 {
  vPickPos = v0 + u * (v3 - v0) + v * (v2 - v0);  
  return vPickPos.z;
 }

二.如果是仅是求地表某点高度
则可用uv重心求法(注意上述第一点的d3d的那个uv重心求法不太一样,他的uv跟他的向量走?)
 float u = (fX-(int)fX);
 float v = (fY-(int)fY);

 float p0 = fHeight[0];
 float p1 = fHeight[1];
 float p2 = fHeight[2];
 float p3 = fHeight[3];

 if(u>v){ // 右边
  return p0 + u * (p1 - p0) + v * (p3 - p1);; 
 }
 else{  // 左边
  return  p0 + u * (p3 - p2) + v * (p2 - p0);
 }

 

11月12日

d3d中实现简单的水

1. 最简单的一张带alpha的贴图直接uv动画
2. 另一种是使用序列贴图, 即水的动画贴图,可在每个自定义大小的格子播放相同的一帧图片来实现.

tt
3. 反射水面:
   上述其中一种方法再加上带bump map效果的反射图

water 

下面给出最后一种具体例子:

用d3d固定管道实现bump mapping振动/移动的水:

(注意:该例子只用D3DTOP_BUMPENVMAP没用D3DTOP_BUMPENVMAPLUMINANCE):
  // 混合状态:
  m_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
  m_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
  m_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);

  // 贴图基台0:
  // 贴图因子(见后面的alpha参数D3DTA_TFACTOR):
  m_pD3DDevice->SetRenderState(D3DRS_TEXTUREFACTOR, 0x90000000);
  // 设置Bump map: 就是一些uv偏移值,保存在bump贴图中(还可以保存一个附加的光照值)
  m_pD3DDevice->SetTexture(0, m_pTexBump);
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_BUMPENVMAP);
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTA_DIFFUSE);//D3DTOP_DISABLE);
                // bump矩阵(参见上面msdn链接说明):
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_BUMPENVMAT00, F2DW(0.05f));
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_BUMPENVMAT01, F2DW(0.0f));
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_BUMPENVMAT10, F2DW(0.0f));
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_BUMPENVMAT11, F2DW(0.05f));
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_BUMPENVLSCALE, F2DW(1.0f));
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_BUMPENVLOFFSET, F2DW(0.0f));
                // uv矩阵(uv动画用):
  m_pD3DDevice->SetTransform(D3DTS_TEXTURE0, &m_MatTransEau);
  m_pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2);

                // 贴图基台1:
                // 反射图(可实时计算):
  // Etage 1 : affichage de la texture de r閒lexion
  m_pD3DDevice->SetTexture(1, m_pEnvMiroir->GetMiroir());
                //
  m_pD3DDevice->SetTextureStageState (1, D3DTSS_COLOROP,   D3DTOP_SELECTARG1);
  m_pD3DDevice->SetTextureStageState (1, D3DTSS_COLORARG1, D3DTA_TEXTURE);
  m_pD3DDevice->SetTextureStageState (1, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);
  m_pD3DDevice->SetTextureStageState (1, D3DTSS_ALPHAARG1, D3DTA_TFACTOR);
                // uv矩阵(投影矩阵,将贴图映射到水面用):
  m_pD3DDevice->SetTransform(D3DTS_TEXTURE1, &m_pScene->GetCameraCourante()->GetMatriceProjectionTexture());
  m_pD3DDevice->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION);
  m_pD3DDevice->SetTextureStageState(1, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_PROJECTED |D3DTTFF_COUNT3);
  m_pD3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSU,  D3DTADDRESS_CLAMP);
  m_pD3DDevice->SetSamplerState(1, D3DSAMP_ADDRESSV,  D3DTADDRESS_CLAMP);

                // set fvf, vb 和 ib:
  m_pD3DDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
  m_pD3DDevice->SetStreamSource(0, m_pVB, 0, sizeof(CUSTOMVERTEX));
  m_pD3DDevice->SetIndices(m_pIB);

                // draw:
  m_pD3DDevice->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, NB_VERTEX_EAU, 0, NB_TRIANGLE_EAU);

提示:

上述的bump矩阵只是简单的偏移,实际的uv动画使用了另一个贴图uv矩阵(每帧更新,使得uv移动)来设置的,

另外一个更好技巧是可以使用bump矩阵直接应用sin函数,这样会使用得水波有一定的振动,效果挺好:

   D3DXMATRIX matBumpMat;

   const static DWORD startick(GetTickCount());
   DWORD dwtime=GetTickCount()-startick;
   float time=(float)dwtime/1000;
   FLOAT r = 0.005f;
   matBumpMat._11 = r*cosf(time*8.0f);
   matBumpMat._12 = r*sinf(time*8.0f);
   matBumpMat._21 = r*sinf(time*8.0f);
   matBumpMat._22 =-r*cosf(time*8.0f);

   m_pD3DDevice->SetTextureStageState(0,D3DTSS_BUMPENVMAT00,F2DW(matBumpMat._11));
   m_pD3DDevice->SetTextureStageState(0,D3DTSS_BUMPENVMAT01,F2DW(matBumpMat._12));
   m_pD3DDevice->SetTextureStageState(0,D3DTSS_BUMPENVMAT10,F2DW(matBumpMat._21));
   m_pD3DDevice->SetTextureStageState(0,D3DTSS_BUMPENVMAT11,F2DW(matBumpMat._22));


上述方法需要检测是否支持 D3DTEXOPCAPS_BUMPENVMAP

关于bump mapping参见msdn:

1. Bump Mapping (Direct3D 9)
http://msdn2.microsoft.com/en-us/library/bb172379.aspx

2. Bump Mapping Formulas (Direct3D 9)
http://msdn2.microsoft.com/en-us/library/bb172380.aspx

3. Bump Map Pixel Formats (Direct3D 9)
http://msdn2.microsoft.com/en-us/library/bb172381.aspx

4.Using Bump Mapping (Direct3D 9)
http://msdn2.microsoft.com/en-us/library/bb206304.aspx

原理参见第2点,使用方法参见第4点

 

附:

关于bumpmap图是可以直接程序创建的,下面给出该方法的代码(这是从 盖小房(BYOH)source code中看到的,直接贴这里可参考):

LPDIRECT3DTEXTURE9 CreateBumpMap(LPDIRECT3DDEVICE9 device,  DWORD dwWidth, DWORD dwHeight,D3DFORMAT d3dBumpFormat )
{
 LPDIRECT3DTEXTURE9 psBumpMap;

// Create the bump map texture
 if( FAILED( device->CreateTexture( dwWidth, dwHeight, 1, 0/* Usage */,
  d3dBumpFormat, D3DPOOL_MANAGED,&psBumpMap,NULL ) ) )
  return NULL;

// Lock the surface and write in some bumps for the waves
 D3DLOCKED_RECT d3dlr;
 psBumpMap->LockRect( 0, &d3dlr, 0, 0 );
 CHAR* pDst = (CHAR*)d3dlr.pBits;
 CHAR  iDu=0, iDv=0;

 for( DWORD y=0; y<dwHeight; y++ )
 {
  CHAR* pPixel = pDst;
  for( DWORD x=0; x<dwWidth; x++ )
  {
   FLOAT fx = x/(FLOAT)dwWidth - 0.5f;
   FLOAT fy = y/(FLOAT)dwHeight - 0.5f;
   FLOAT r = sqrtf( fx*fx + fy*fy );
//  iDu  = (CHAR)( 32 * cosf( 300.0f * r ) * expf( -r * 5.0f ) );
   iDu += (CHAR)( 128 * cosf( 100.0f * ( fx + fy ) ) );
//  iDu += (CHAR)( 16 * cosf( 140.0f * ( fx * 0.85f - fy ) ) );
//  iDv  = (CHAR)( 32 * sinf( 300.0f * r ) * expf( -r * 5.0f ) );
   iDv += (CHAR)( 128 * sinf( 100.0f * ( fx + fy ) ) );
//  iDv += (CHAR)( 16 * sinf( 140.0f * ( fx * 0.85f - fy ) ) );
   *pPixel++ = iDu;
   *pPixel++ = iDv;
  }
  pDst += d3dlr.Pitch;
 }
 psBumpMap->UnlockRect(0);

 return psBumpMap;
}

11月8日

区域选择显示图

最近做的魔兽里区域选择显示图, 这个与地面贴花不一样,要求边框不会因为区域放大而跟着

缩放(与魔兽不一样的是中间不用小格子省点顶点), 而关于地面贴花可以用匹配地形网格再放绽放uv进行调整

并且设置边框模式或clamp模式(图片边框需透明)而简单得到,当然复杂的也可以用投影贴图来作,(类似于实时影子的作法:

先渲染场景深度,再调相机到灯光处渲染需需投影的物件到贴图上,最后将贴图以顶视图方式投影到地形上)

{A751E3AF-7568-4CEE-9B5C-8086E61082FB}{FF92B66A-87B6-4B20-A4C5-C2AEFF66E7E9}