平民程序 - linghuye's blog

天下风云出我辈,一入江湖岁月催。皇图霸业谈笑中,不胜人生一场醉。提剑跨骑挥鬼雨,白骨如山鸟惊飞。尘事如潮人如水,只笑江湖几人回。

随笔 - 221, 文章 - 0, 评论 - 680, 引用 - 0
数据加载中……

优雅的代码

程序写到了一定阶段,我很喜欢写清晰优雅的代码.优雅的代码易于理解和维护,并给人以审美上的愉悦,于是我以为这就是健壮的代码.

但我看过很多的代码,特别是一些产品级的代码,以我的审美而言,长得大都很丑,但它们都很有效很健壮.

后来我知道了为什么,优雅清晰的代码对健壮性的贡献可能还不到30%的,而真正健壮的代码是那些在市场上跑上半年一年的,经受各种磨难摧残的代码,这些代码为了生存,被打上了各式各样的丑陋的补丁,以对付现实环境中那些邪恶的攻击,丑陋的硬件缺陷,木马横行的客户机.

这好比,一个人在学院里学得再好的为人修养之道,也不如在那些在各种逆境中经历人生磨难的人在遇到危难时来得更为沉着,因为他们不知道现实世界的危险. 在外部危险的环境下我们要写懂得顽强生存的代码,而不是优雅的代码.

posted @ 2007-04-29 19:43 linghuye 阅读(4219) | 评论 (12)编辑 收藏

ModelView矩阵的含义和应用

设OpenGL的镜头坐标系的三个轴为(R,U,N),这里的R,U,N的数值表达在世界坐标系上,表达为*行*向量.则OpenGL用gluLookAt建立的ViewMatrix为:
  R
( U ). 即ViewMatrix的第一行为R,第二行为U,第三行为-N.
 -N
假设当前的ModelMatrix为单位矩阵,当我们使用glGetFloatv(GL_MODELVIEW_MATRIX, &mat),即取回ViewMatrix,然后将上述规则与mat进行比对,发现,mat的第一列是R,第二列是U,第三列是-N.

这是因为OpenGL使用该死的列存储方式,而上述规则使用行存储方式表达.行存储或列存储完全是计算机中数组的内存的行优先或列优先的排布问题(怎么会有人习惯列优先存储这种变态的思维),与空间数学概念(如列向量,行向量)无关.

在大多公告板实现的代码,都有glGetFloatv(GL_MODELVIEW_MATRIX, &mat); 然后从各列中解析出R,U,N的步骤,接着用R,U,N作公告板计算.
而当我们使用glGetFloatv(GL_TRANSPOSE_MODELVIEW_MATRIX, &mat),才与上述规则相一致.

在DirectX中D3DXMatrixLookAtLH建立出的ViewMatrix为(R, U, N),这里的R,U,N是列向量,即ViewMatrix的第一列是R,第二列是U,第三列是N.
DirectX没有令人误导的列存储方式,生成的ViewMatrix确实符合这个数学标准.

OpenGL中能直接取到的是ModelViewMatrix,此矩阵将模型坐标从模型坐标系转换到镜头坐标系,ModelViewMatrix的逆矩阵可以将镜头坐标系下的坐标转换到模型坐标系,一个典型的应用是:已知在镜头坐标系下,镜头所在(0, 0, 0)点,则使用上述逆矩阵推算出镜头在模型坐标下的位置.注意要想推算镜头在世界坐标系下的位置,需要使用ViewMatrix的逆矩阵.
如果ModelMatrix为标准矩阵,则模型坐标系和世界坐标系重合,ModelViewMatrix矩阵是纯粹的ViewMatrix.

定理:若有一个以当前坐标系为数值参考,而定义的基(以标准数学形式给出)
      R
则( U ) (R,U,N为行向量)矩阵将当前坐标系的点的坐标数值转换到新基的坐标系下的
      N       点的数值.

posted @ 2007-04-29 19:28 linghuye 阅读(4499) | 评论 (1)编辑 收藏

收集目前GLSL在ATI显卡下的BUG

最近升级了机器,由于要等Geforce 8800降价,于是先搞了块ATI X1650XT,也方便程序的兼容性调试.
于是,很多先前写的GLSL Shader出错了.以下均以最新驱动为标准.

1.无法对uniform mat4 boneMatrices[57];数组中的单个矩阵赋值!
 GLint location = glGetUniformLocationARB(m_idProg, "boneMatrices[2]");
 glUniformMatrix4fvARB(location, 1, GL_TRUE, fMatrices);
取得location有效,赋值后在Shader中验证,赋值没有成功.

2.如果外部没有显式地设置纹理矩阵,则gl_TextureMatrix[0]没有定义,不是默认的单位矩阵.

3.如vec4 v; ATI没有默认初始化为vec4(0, 0, 0, 0); 而Nvidia下则有默认初始化.

4.GLSL数组的索引变量必须为编译期常数,否则:
 Link successful. The GLSL vertex shader will run in software - unsupported language element used.
CPU模拟运行,速度奇慢无比.这还是ATI X1650XT级别的显卡,而NVidia 6000系列就可以支持dynamic array index了.
没有了dynamic array index,是根本无法作骨骼动画计算的,而同样的显卡下HLSL却支持dynamic array index,所以这不是显卡能力问题,是态度问题.

 但严格来说,这些都不能算ATI的bug过错,他确确实实没有违背GLSL的语言标准, 最多也就是消极应对,极不友好而已.因为我去翻看这些GLSL语言标准,发现这些细节地方的描述十分含糊,基本上是可有可无,OpenGL ARB整一个官僚.

GLSL毁于ARB手.

补:
在Openg.org上发帖查问,一位哥们给出了4的优化问题,改成
vec4 blendVertex = vec4(0, 0, 0, 0);
for(int i = 0; i < 4; i++)

      blendVertex += boneMatrices[int(bone[i])] * gl_Vertex * weight[i];
}
于是这段shader神奇地run in hardware mode了,原来是shader里的loop逻辑过于复杂导致的run in software mode,却表现在了dynamic index上,似乎只要屏蔽dynamic index就正常.

posted @ 2007-04-05 21:20 linghuye 阅读(1715) | 评论 (1)编辑 收藏

float的格式

float是32位浮点,
Sign Bit S             Biased Exponent E             Packed Mantissa M
1 Bit in position     8 Bits in positions 30-23     23 Bits in positions 22-0
最终值为: (-1)^S  *  2^(E-127)  *  (1 + M/(2^23))
附加规则:
1.若E = 0 且 M = 0,表示值为0,所以有正负两个0.
2.若E = 0 且 M <> 0,表示值为(-1)^S  *  2^(E-127)  *  (M/(2^22)).这种值称作denormal值,VC7内存观察器查看float值,后面跟着DEN就是这个意思.
3.若E = 255 且 M = 0,则表示值为无穷大.
4.若E = 255 且 M <> 0,则表示值为Not-a-Number.若M的最高位为0,则表示值为SNaN.若M的最高位为1,则表示值为QNaN.

posted @ 2007-03-20 18:13 linghuye 阅读(1549) | 评论 (0)编辑 收藏

过年奖励给自己买书

 



More OpenGL Game Programming,好书啊,非常适合我现在的程度, Intermediate to advanced,I love OpenGL! 
3D Game Engine Design(Second Edition),现在有1000多页了,全面转向设计一个基于可编程管线的引擎,好书啊,好重的书啊.
ShaderX4,2006年前沿奖获得者,好书啊,其余两本,咳,好书啊,好重的书啊,好贵的书啊......
看着这些书,突然感想,要多写可累积实用的代码,不要写demo式的代码.

另,在Amazon上用信用卡买书,真是怕怕,不用密码,卡号+到期时间就可以了,而国内商场签单时都光明正大地把那个卡号打印出来.

榨干书中的每个字!

posted @ 2007-03-04 23:09 linghuye 阅读(1886) | 评论 (8)编辑 收藏

3D程序员的痛苦

做个3D程序,痛苦的不是做不出那些复杂绚丽的画面效果,最痛苦的莫过于面对无奇不有的玩家机器运行环境,不同的显卡,不同的驱动程序,还有那不同的木马病毒,不同的杀毒软件.有一天你的程序不幸阵亡了,我们可爱的玩家到论坛上骂一句,合理地表达一下他愤怒的心情,说那个程序躺尸躺的很是恶心,然后挥挥手不留下一片云彩,只留下我在那里红着脸,低着头,绞尽脑汁地在代码里检讨自己可能的错误. 由于无法重现这个邪恶的Bug,而且自己周围的机器都表现的很善良,所以剩下的技术手段就是纯粹靠猜.于是变得格外多疑敏感和恶心,怀疑这个API有问题,怀疑那个驱动程序版本有问题,怀疑代码里这个优化太过分,那个地方不够兼容,整天神经兮兮.

绝大多数的国内游戏玩家是不知道怎么安装显卡驱动程序的,你请他安装驱动程序,他是会直接把游戏删除的,得罪不起.所以即使驱动程序有差误,程序也应当兼容错误,嗯,这个要求的难度相当高.

我非常的憎恨ATI的显卡,因为几乎所有的问题都出在ATI显卡上.喜欢MyWarcraftStudio的朋友请以后购买显卡时优先考虑Nvidia的,当我在公司红着脸时,我很想回家在MyWarCraftStudio里加上拒绝运行在ATI显卡上以示报复,我自己的程序我想不支持那个显卡我就不支持,比微软都牛:). 我看WoWModelViewer的作者大部分时间也都在解决各种各样的和显卡有关的Bug,有段时间都不想在搞下去了,确实写这种非官方程序只是图个乐,自己happy,大家happy,太多的责任最后就很厌倦了.

以前我相信这种论调,说DirectX对显卡兼容性比OpenGL好,现在我觉得DirectX的兼容性比OpenGL乱.DirectX是有标准,但在Intel 845G集成显卡的操作系统上安装了DirectX9,不等于它就支持了DirectX 9.有时那些Caps也是乱七八遭,如在有的ATI显卡上,caps参数显示支持抗锯齿,可一旦打开抗拒齿,崩溃的崩溃,花屏的花屏. DirectX的那个设备丢失最恶心了,Resize一下窗口设备都会丢失,分明是个架构设计上的Bug,死活不改,还搞出个对付DeviceLost的专题来,每个对象再来个OnDeviceLost接口,奇丑无比.

我一直希望NVIDIA或ATI能出个能模拟各种显卡的驱动程序,这样能稍微缓解一下测试问题.有时我也跑到网吧去测试程序,不过现在网吧的显卡配置都高,而且型号统一,都是主流显卡,以后得找个比较破的野店才行.

做3D,安全第一.

posted @ 2007-02-15 20:52 linghuye 阅读(5695) | 评论 (8)编辑 收藏

OpenGL/DirectX渲染技巧集

1.树叶/动物嘴上的牙齿,胡须/身上的鬃毛,形状曲折复杂,若直接用三角形带构建会大大增加物体的三角形数,所以应使用少量的三角形加Alpha纹理来实现.过滤了Alpha的纹理可以描绘出曲折的形状边缘.
2.怪物眼睛发光,那是用了额外附着的不透明的纹理三角片,很可能还是billboard.
3.该GL_CULL_FACE的地方应该Cull Face,以提升效率.
4.BLEND_MODULATE_2X使盔甲呈现高亮, BLEND_ADDITIVE_ALPHA增加加物体细节.
5.WoW的登录界面就是个m2 + xml + lua,一个模型搞定一个界面.
6.在3D空间中画一个中间镂空的平面边形(由三角形Mesh拼接而成),纹理使用全黑,SrcBlend=ZERO,DstBlend=ONE,这样渲染后,在画面上完全没有起变化,但是DepthBuffer起变化了,画过的地方已经出现了深度值,人物走到这地方会出现被透明的墙挡住的效果.

posted @ 2007-02-08 20:01 linghuye 阅读(4839) | 评论 (6)编辑 收藏

Billboard思想和实现整理

Billboard似乎是比较简单的技术,实际动手才知道相当复杂,最后把自己绕晕了.基础不扎实,没应用到实际开发中的知识随时都会忘记.有些技巧则无法探求其本源,大部分人都知道如何按技巧公式作出一个公告板,而不知其所以然.

In each billboard techniche, a target surface normal and an up direction are found for orienting the polygon, usually a quadrilateral.
Then M = (r, u, n)(r is right vector, u is up vector, n is normal vector, three all are said as to the target billboard plane),is the matrix transform a quadrilateral in the xy plane with +y as pointing towards its top edge.

就是说公告板的原始顶点要用xy平面来定义,M = (r, u, n)才是其对应的公告板矩阵.所以几乎所有的公告板实现为什么都是[x-d, y-d] - [x+d, y+d],而不是[x-d, z-d] - [x+d, z+d].

Screen-Aligned billboard的n是镜头视平面法线的逆方向,u是镜头的up.
Axial Billboard的u是受限制的Axial, r = u * n,(n是镜头视平面法线的逆方向,或,视线方向的逆方向),最后再计算一次n' = r * u,即n'才是最后可行的代入M的n,表达了受限的概念.
World-oriented billboard就不能直接使用镜头的up做up,因为镜头roll了,并且所画的billboard原本是应该相对世界站立的,按Screen-Aligned的做法就会随镜头旋转,所以此时应该r = u * n(u是其在世界上的up,n是镜头视线方向的逆方向),最后再计算一次u = r * n,即u'才是最后的up,即非物体本身相对世界的up,亦非镜头的up.

在应用粒子系统时有个很流行的技巧,
http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat2
这种技巧是view plane oriented(视平面对齐),而非view oriented(视点对齐).
对于粒子系统而言,如果使用view oriented,由于每个粒子都有自己的position和vLooAt,有自身的billboard矩阵,需要作一次transform计算,消耗就很大.而对于粒子系统这种使用视平面对齐(就是与屏幕对齐)即可以达到效果的应用,使用这技巧可以避免对每个粒子作一次transform计算,既然所有粒子都面朝一个视平面,其billboard都是一致的.而技巧公式只是上述原理的简化推导.对于大物体Impostor,这种面向视平面的技巧并不适合.

所以公告板技术是一种非常复杂的技术,它的实现变种实在太多了.归其根本在于,
1.View Oriented / View plane oriented
2.Sphere / Axial
3.Camera up / World up


公告板的原始顶点要用xy平面来定义
原始顶点的含义应该是经过骨骼运动后的顶点,骨骼运动后的顶点才是真正要做billboard的顶点. 若经过骨骼运动后的面没有朝向XY面,应该立刻附加一个矩阵使其朝向XY面,之后才能做Billboard.

// Billboard矩阵的根本: 正面镜头旋转 + 中心位置
D3DXMATRIX matBillboard( vRight.x, vRight.y, vRight.z, 0.0f,
            vUp.x,  vUp.y,  vUp.z,  0.0f,
            vLookat.x, vLookat.y, vLookat.z, 0.0f,
            matTransform._41, matTransform._42, matTransform._43, 1.0f);
前提是模型原始顶点或原始顶点预变换后,在乘以matBillboard之前,应该是在XY平面内的,且以原点为中心.


Reference:
http://www.lighthouse3d.com/opengl/billboarding/index.php?billCheat1
More OpenGL Game Programming
Realtime Rendering
http://www.cnitblog.com/linghuye/archive/2007/04/29/26402.html

posted @ 2007-02-07 23:11 linghuye 阅读(4366) | 评论 (1)编辑 收藏

DrawIndexedPrimitiveUP这个函数

最近被DrawIndexedPrimitiveUP这个函数搞崩了好几次,甚至连显卡的Blue Screen Of Death也出来凑热闹了,所以需要总结一下它崩溃的可能原因,理清下头绪:
1.与OpenGL的glXXXPointer一样,如果指针内存错误,崩溃是肯定的,这种bug比较好找.
DirectX的原因更复杂:
2.DrawIndexedPrimitiveUP不直接崩溃,而是在Present的时候崩溃.可能SetIndices的索引缓冲指针是野指针,LPDIRECT3DVERTEXDECLARATION9是野指针.

解决方法:使用Debug Runtime排除所有异常调用,这对显卡驱动程序蓝屏崩溃或异常现象的调试十分有效,如:
Direct3D9: Decl Validator: X249: (Element Error) (Decl Element [5]) Declaration can't map to fixed function FVF because blendweight must use D3DDECLTYPE_FLOAT1/2/3/4.
Direct3D9: (ERROR) :DrawIndexedPrimitive failed.

3.又发现一种DrawIndexedPrimitiveUP错误,当错误地把索引个数作为第2个参数传入(实际上第2个参数应该是顶点个数), 导致程序在运行一段不定时间,随机地,可能地,花屏了.此时DX8报告一个驱动程序的错误:
Direct3D8: (ERROR) :Driver not handled in DrawPrimitives2
Direct3D8: (ERROR) :Driver returned error: DDERR_INVALIDPARAMS  
Direct3D8: (ERROR) :Driver failed command batch. Attempting to reset device state. The device may now be in an unstable state and the application may experience an access violation.

另:在试图安装现在的DirectX Debug Runtime时发现先前网上的资料或MSDN里都没谈到现在的安装方法:把Developer Runtime\x86里的dll复制到System32目录,然后再运行Utilities\Bin\x86\dxcpl.exe切换到Debug Rumtime模式.运行REF模式需要Debug Runtime环境!

Reference:
http://linghuye.googlepages.com/MyownlittleDirectXFAQ.mht

posted @ 2007-02-03 00:07 linghuye 阅读(3460) | 评论 (0)编辑 收藏

MyWarCraftStudio 0.10.6337

1.Update MyWarCraftStudio to 0.10.6337 for supporting Burning Crusade final formal client 2.0.6.6337.
2.It should be compatible with the normal 1.12 wow client.
3.I try to experiment DirectX rendering in this version. Change the Cnfg\MyWarCraftStudio.xml about <RenderEngine API="OpenGL"/> to <RenderEngine API="DirectX"/>. Be aware of bugs.

http://www.cnitblog.com/Files/linghuye/MyWarCraftStudio_0.10.6337.rar
http://linghuye.googlepages.com/MyWarCraftStudio_0.10.6337.rar

posted @ 2007-01-28 15:18 linghuye 阅读(41437) | 评论 (22)编辑 收藏

仅列出标题
共23页: First 3 4 5 6 7 8 9 10 11 Last