|
|
July 26 像素格式明确了OpenGL绘制平面的特性,如象素缓冲区是单缓冲还是双缓冲,数据是 RGBA方式还是Color Index方式等。 每个OpenGL显示设备一般用名为PIXELFORMATDESCRIPTOR的结构来表示某个的像素 格式,这个结构包含26个属性信息。Win32定义PIXELFORMATDESCRIPTOR如下所示: typedef struct tagPIXELFORMATDESCRIPTOR { // pfd WORD nSize; WORD nVersion; DWORD dwFlags; BYTE iPixelType; BYTE cColorBits; BYTE cRedBits; BYTE cRedShift; BYTE cGreenBits; BYTE cGreenShift; BYTE cBlueBits; BYTE cBlueShift; BYTE cAlphaBits; BYTE cAlphaShift; BYTE cAccumBits; BYTE cAccumRedBits; BYTE cAccumGreenBits; BYTE cAccumBlueBits; BYTE cAccumAlphaBits; BYTE cDepthBits; BYTE cStencilBits; BYTE cAuxBuffers; BYTE iLayerType; BYTE bReserved; DWORD dwLayerMask; DWORD dwVisibleMask; DWORD dwDamageMask; } PIXELFORMATDESCRIPTOR;
nSize是象素格式描述子结构的大小,sizeof(PIXELFORMATDESCRIPTOR)设定其值; nVersion是PIXELFORMATDESCRIPTOR结构的版本,一般设为1; dwFlags是一组表明象素缓冲特性的标志位,如缓冲是否支持GDI或OpenGL等; iPixelType 说明象素数据类型是RGBA还是颜色索引; cColorBits 每个颜色缓冲区中颜色位平面的数目,对颜色索引方式是缓冲区大小; cRedBits 每个RGBA颜色缓冲区中红色位平面的数目; cRedShift 每个RGBA颜色缓冲区中红色位平面的偏移数; cGreenBits 每个RGBA颜色缓冲区中绿色位平面的数目; cGreenShift每个RGBA颜色缓冲区中绿色位平面的偏移数; cBlueBits 每个RGBA颜色缓冲区中蓝色位平面的数目; cBlueShift 每个RGBA颜色缓冲区中蓝色位平面的偏移数; cAlphaBits 每个RGBA颜色缓冲区中alpha位平面的数目(保留的,现不支持); cAlphaShift每个RGBA颜色缓冲区中alpha位平面的偏移数(保留的,现不支持); cAccumBits 累加缓冲区中全部位平面的数目; cAccumRedBits 累加缓冲区中红色位平面的数目; cAccumGreenBits累加缓冲区中绿色位平面的数目; cAccumBlueBits 累加缓冲区中蓝色位平面的数目; cAccumAlphaBits累加缓冲区中alpha位平面的数目; cDepthBits Z(深度)缓冲区的深度; cStencilBits 模板缓冲区的深度; cAuxBuffers 轴向缓冲区的数量(一般1.0版本不支持); iLayerType 被忽略,为了一致性而包含的; bReserved 表层和底层平面的数量::位0-3表最多15层表层平面,位4-7表底层; dwLayerMask 被忽略,为了一致性而包含的; dwVisibleMask 是透明色彩的值(RGBA方式)或是一个底层平面的索引(Index); dwDamageMask被忽略,为了一致性而包含的。
常用值示例: PIXELFORMATDESCRIPTOR src = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | // support window PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type 24, // 24-bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer 0, // shift bit ignored 0, // no accumulation buffer 0, 0, 0, 0, // accum bits ignored 24, // 24-bit z-buffer 8, // 8-bit stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; Windows提供了四个像素格式管理函数,分别介绍如下: (1) int ChoosePixelFormat(HDC hdc, PIXELFORMATDESCRIPTOR *ppdf) 该函数比较传过来的像素格式描述和OpenGL支持的像素格式,返回一个最佳匹配的像素格式索引。该索引值可传给SetPixelFormat为DC设置像素格式。返回值为0表示失败。 在比较像素格式时,匹配优先级顺序为像素格式描述子结构中的下述各域: dwFlags->cColorBits->cAlphaBits->cAccumBits ->cDepthBits->cStencilBits->cAuxBuffers->iLayerType 硬件支持的像素格式优先。 (2) int DescribePixelFormat(HDC hdc, int iPixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR *ppfd) 该函数用格式索引iPixelFormat说明的像素格式来填写由ppfd所指向的像素格式描述子结构,利用该函数可以枚举像素格式。 (3) int GetPixelFormat(HDC hdc) 该函数用于获取hdc的格式索引。 (4) BOOL SetPixelFormat(HDC hdc, int iPixelFormat, LPPIXELFORMATDESCRIPTOR *ppfd) 该函数用格式索引iPixelFormat来设置hdc的像素格式。在使用该函数之前应该调用ChoosePixelFormat来获取像素格式索引。另外,OpenGL窗口风格必须包含WS_CLIPCHILDREN和WS_CLIPSIBLINGS类型,否则设置失败。 应该注意的是ChoosePixelFormat函数并不一定返回一个最佳的像素格式值,可以利用DescribePixelFormat来枚举系统所支持的所有像素格式。OpenGL的通常支持24种不同的像素格式,如果系统安装了OpenGL硬件加速器,它可能会支持其它的像素格式。
July 25 幸好这两天代码不多,当调试进程结束时遇到了这个错误,中断到了ntdll内,没有办法跟踪堆栈。 而非调试状态运行程序反而没有这个错误。当排出掉自己写的 int 3 之后(程序中也可以自己写断点中断)。感觉是退出的时候有些问题,在网上查找了一下,有人是DestroyWindow时窗口句柄不存在了。看了一下自己相关的代码没什么问题。无意间想到了在接受WM_CLOSE 消息时,调用了PostQuitMessage(0),case 块后面写成了 break ,进而调用了默认的消息处理进程。把它修改为return 0 之后,问题解决了。不知道是不是还有其他人遇到其它的情况,欢迎拍砖探讨 HDC hDC; int colorBits, xRes, yRes; hDC = GetDC( GetDesktopWindow() ); //获得当前桌面进程DC colorBits = GetDeviceCaps( hDC, BITSPIXEL ); //获得色深,如32位真彩色返回32 xRes = GetDeviceCaps( hDC, HORZRES ); //获得分辨率宽度 yRes = GetDeviceCaps( hDC, VERTRES ); //获得分辨率高度 ReleaseDC( GetDesktopWindow(), hDC ); //释放DC
修改分辨率: 在VC中提供了修改显示设备(如显示器、打印机等等,本文只就显示器而言)属性的函数:ChangeDisplaySettings,该函数能够按照你的需要对显示设备作出相应的修改。其函数申明如下: LONG ChangeDisplaySettings(LPDEVMODE lpDevMode, DWORD dwflags); 其参数的含义如下: lpDevMode:一个指向DEVMODE数据结构的指针,DEVMODE的数据结构描述了欲设定显示器的各类属性值。通常情况下使用到的参数有: dmSize:所用DEVMODE数据结构的大小(以Bytes为单位) dmBitsPerPel :每象素所使用的显存位数(Bits) dmPelsWidth Pixel width :水平分辨率(点数) dmPelsHeight Pixel height :垂直分辨率(点数) dmDisplayFrequency Mode frequency :显示刷新率,以赫兹为单位 dmFields:通常情况下,不同的显示设备(如打印机)用到的DEVMODE数据结构的内容不同,比如设定打印机时,你不会用到dmDisplayFrequency属性。所以,在你使用DEVMODE数据结构时,应向系统说明你具体用到的有效数据成员,dmFields的用处便在于此。如果在程序中只用到dmPelsWidth(水平分辨率)和dmPelsHeight(垂直分辨率),那么该值应为DM_PELSWIDTH|DM_PELSHEIGHT。DM_DISPLAYFREQUENCY 为刷新频率 DM_BITSPERPEL 表示色深 Dwflags:表明对显示设备的修改方式。具体取值有以下几种: 0 :动态改变显示设备属性 CDS_UPDATEREGISTRY:动态改变显示设备属性并修改注册表相关设置,下次启动计算机时,本次所做的修改依然有效 CDS_TEST: 测试所做的修改是否有效 CDS_FULLSCREEN: 该函数的返回值: DISP_CHANGE_SUCCESSFUL:修改成功 DISP_CHANGE_RESTART :修改后需重新启动(在显示器设定中选择了“应用新的颜色前重新启动计算机”) DISP_CHANGE_FAILED :修改失败 DISP_CHANGE_BADMODE:修改模式错误(比如你的显示器是单色的,但你却将之修改为256色的)
当If lpDevMode为NULL且dwflags 为0时, 显示设备使用注册表当前值。ChangeDisplaySettings(NULL,0)
July 19 程序报错,中断后堆栈中只有00000000().注意最后的()在vc里很容易被看成0. 造成这种错误的一种情况是: 1. 调用结构中的成员,此成员为函数指针。但指针没有赋值或者值为NULL。而当调用这个函数的时候会跳出这样的错误情况 2. 同样在OnTimer 定时器函数中调用一个指向NULL的函数指针也会发生这种错误 3. 声明了函数指针,但未给其赋值。通过extern 外部引用了这个指针函数。当调用函数时也会出现这种错误 终于看到了quake3 的Renderer项目中的代码,发现了这样一段: if ( (int)tess.xyz & 15 ) { Com_Printf( "WARNING: tess.xyz not 16 byte aligned\n" ); } xyz 是一个矢量(float [4]) 数组。 当我自己的工程 Release 编译时,进入了这个警告。这个警告的意思是 tess的结构成员 xyz 所在的内存地址不是16byte对齐的。而对于quake3 的工程编译后却没有进入这个警告。直觉告诉我肯定是一些vc工程的设置问题,经过层层筛选,最后终于定位到。默认的vc release 工程,链接器-〉优化-〉引用:选择的是/OPT:REF,这个可以优化掉不用的函数减少文件大小。但Renderer 项目作为quake3 项目的依赖项,这样作用并不大。相反这个优化反而导致了内存变化。对于游戏程序来说,矢量对齐到16byte可以提高一定游戏性能。尽管可以在xyz 的声明前加上__declspec (align(16)),强制这个变量内存对齐到16byte边界。但需要修改的地方实在太多。 如果你的程序不需要计较多几K大小,这个选项还是选为默认吧。即不对其进行优化. 注意:由于quake3变量顺序安排的巧妙,才使tess.xyz 对齐到16位内存上的。如果改变了变量定义顺序,或者打开了上面的优化。都会导致tess.xyz不能对齐到16byte.如果自己不愿去计算,还是使用__declspec (align(16)) 吧 刚把所有blog 整理个列表,ms 就更新了。美其名曰:摘要,一页能浏览20个选项。真是。。。
July 14 指针的指针总是容易让人困惑,更能带给你惊奇. typedef struct nodetype { struct nodetype **head; } node_t; typedef struct { node_t** freelist; node_t* nodePtrs[768]; } huff_t; static void free_ppnode(huff_t* huff, node_t **ppnode) { *ppnode = (node_t *)huff->freelist; //让 ppnode 所在的节点存放 freelist 内存地址 huff->freelist = ppnode; //freelist 指向第一个节点 } //获取一个自由的node_t* 节点,用来存储 head (指向一个块中具有最大节点编号的节点) static node_t **get_ppnode(huff_t* huff) { node_t **tppnode; if (!huff->freelist) { return &(huff->nodePtrs[huff->blocPtrs++]); } else { tppnode = huff->freelist; //freelist 指向的内存。*tppnode 中存贮着下一个自由节点地址 huff->freelist = (node_t **)(*tppnode); //freelist 节点指向下一个free node地址,注意强制转换。 return tppnode; } } free_ppnode 每次回收ppnode的时候,在其指向的空间(nodePtrs[768] 中的元素)内容就不需要了,而这时修改ppnode指向空间中的数值(存放的是node_t * 变量,可以暂时认为其为32 位地址数字), 让其存放 freelist 变量值(可以暂时认为其是一个32位地址数字)。这样多次释放之后就形成了一个链表,但不需要任何其他的多余的元素协助。而普通链表中的需要 next 指针协助才能连接两个链表节点。 当然需要从链表取得下一个元素时,需要对 freelist 进行提领操作来获得以前的freelist 变量值 July 12 下面是一段常用的代码,而当使用 ctrl+shift 切换紫光输入法,输入文字。反复切换两次之后。GetMessage 系统函数取消息时冲突。而使用其它输入法都没有问题。经过 google 发现 firefox 也和紫光冲突,也有人和操作系统冲突的。说明此输入法 hook msg 有问题,不得不说紫光输入法确实好用。但下面的代码几乎每个mfc程序都差不多到了,或者有些程序直接调用GetMessage,而没有使用PeekMessage。冲突环境:使用 winxp+sp2 更新了所有系统补丁。紫光拼音3.0 。
如果你是开发人员,还是建议你不要用这个输入法了。
while (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE)) //获得 WM_QUIT 消息 { MSG msg; if ( !GetMessage (&msg, NULL, 0, 0) ) { }
// save the msg time, because wndprocs don't have access to the timestamp
TranslateMessage (&msg); DispatchMessage (&msg); } July 04 凸多边形  所谓凸多边形,就是把一个多边形任意一边向两方无限延长成为一条直线,如果多边形的其他各边均在此直线的同旁,那么这个多边形就叫做凸多边形。如图1,多边形ABCDEF,把线段AF向两方无限延长,此多边形的其他各边AB、BC、CD、DE、EF均在此直线的同旁,所以多边形ABCDEF是凸多边形。 凹多边形  把一个各边不自交的多边形任意一边向两方无限延长成为一直线,如果多边形的其他各边不在此直线的同旁,那么这个多边形就叫做凹多边形。 凹多边形的内角和的解,其实我们可以通过(n-2)·180来计算。实际上是把大于平角的角划分为两个角(如图) | 这不是quake 中bsp实现方式,而是早期doom的。或者说不是3d 关卡的bsp实现方式,希望从简单的2d 开始了解bsp概念的可以看看 | BSP 树 ------ 解释BSP树的运用,最好是从一个例子开始.设想一个很简单的DOOM关卡的例子.
 这个关卡由一个屋子套在另一个屋子里构成.玩家被封闭在矩形ABHG中. 先给出几个定义.(如图)
我们用矢量定义直线,所以
a = (A,B) e = (E,C) f = (C,D) g = (F,D)
当一个点在直线矢量方向的左边时,我们称点在直线的左边.
因此,在这个例子里,a的左边什么也没有;所有的东东都在它的右边.注意这些 依赖与我们对a的定义,如果我们定义 a = (B,A) 则所有的东东都在a的左边.
面是玩家看到的直线的一边.例如墙e,就有两个面(e'和e").不是所有的墙都有 两个面 -- 如果玩家只能看见墙的一面,那么这堵墙就只有一个面.
面是由矢量方向定义的,直线的两个面分别被称作左表面和右表面.
这个例子中的BSP树是这样的:
f / \ / \ / \ a,d1,b1 e / \ / \ / \ d2,c1 g / \ / \ / \ c2 c3,b2
每个节点都是一条直线.所有在直线左边的东东都在它的左子树上,所有在它 右边的东东都在它的右子树上.
注意 d 面不是完全在 f 面的右边或左边.为了描述这种情况,我们把它分为 了两个部分,一个部分放在左子树,一部分放在右子树.因而,我们必须产生新 的面来构造BSP树.
我将在后面解释BSP数是怎样创建的.首先,我将给出使用BSP树来产生一幅画的方法.
假设玩家站在点'x',看着北方.
我们从树的顶端直线 f 开始.我们站在直线 f 的右边,所以我们向树的左子 树进行下去.这是因为我们想最先画最远的多边形.
我们来到了最左的节点.请在笔记本上记下节点上的东东--"a,d1,b1".
当我们不能再往下时,回到父节点.现在回到了根节点,我们还不能马上去右子树. 首先,我们看见了 f 面--写在这个节点上的.我们已经在我们列出的表上得到了 处在它后面的所有东东,我们还将看见它前面的东东,但是我们必须先把它记入我 们的表中.注意,f 面有两个表面--f' 和 f".既然我们已经知道我们处在直线 f 的右边,当然就只能看到它的右表面--所以我们在笔记本上记下 f".现在本子上 写着 a,d1,b1,f".
注意,如果我们是看着南方(视线远离 f 面),看不到 f 的任何一个面和 f 的那 一面后的所有东东.在这种情况下,我们就不必做前面这些.
现在我们向下到节点 e.我们在 e 的右边,所以要往左子树去,这样便得到了一个 叶节点.现在把 d2,c1 记下来.
再回来,看看该记下 e 的哪一面.应该是 e'.现在笔记本上写着 a,d1,b1,f",d2,c1,e'.
向右子树,来到 g 节点.我们在左边,所以向右得到 c3,b2,再回来,检查 g (我 们在左边,应为g'),去最后一个节点得到 c2,回溯,回溯,回溯,回到根节点,遍历 完成.
最后笔记本上写着:
a d1 b1 f" d2 c1 e' c3 b2 g' c2
如果我们以这个次序来画这些墙,将得到正确的图象.建议你使用3D-buffer而 不要用画家算法,这样速度要快的多.
创建 BSP 树 -----------
BSP树完全是递归创建的.唯一的困难是知道何时该停止递归.应该注意到叶节 点将被整个放入表中--因此将一组平面放在一个叶节点上的充分条件是它们能 够以任何次序画出来而不致有错.也就是说,无论玩家站在哪儿,这一组墙之间 都不会被别的挡住.
好吧,让我们开始:选择一个面 f (这个选择相当随便--最好是选一个面,它能 最少的分割其它面.当然,分割是不可避免的).分割 d 面和 b 面,因为它们被 直线 f 分开了.(用DOOM中的说法,去分割区域的线被称为节点线 _nodeline_ )
然后把 f 左边的东东放在左子树,右边也如此:
f / \ / \ / \ a,d1,b1 b2,c,d2,e,g
我们可以不再处理左子树--因为墙 a,d1,b1 构成一个凸多边形,从任意角度 看它们都互不重叠.然而在另一边,平面 e 却使得从特定点去观察平面 d2 会 被其挡住,所以我们从 e 处分开,这就造成了平面 c 的分割,但是同样被分割 的平面 a 却不用被分割,这是因为 a 不在现在分析的平面中.
第二级 BSP 树为:
f / \ / \ / \ a,d1,b1 e / \ / \ / \ d2,c1 b2,c2,g
现在,c1 和 d2 从不重叠,顾而我们将它们作为另一个叶节点.下一步我们 从 g 处分开,将 c2 分成 c2 和 c3,剩下的节点都是叶节点.
下面这棵 BSP 树的最简单运用--再给一个例子来加深印象.考虑一下站在 y 点向北看的情况.因为看不到 f 面,你只用搜索左子树.这样马上就得到 了需要的循序: a,d1,b1.
精华 ----
如果我们在每个节点上为每个子树定义一个特定空间,记录子树中的信息, 这样我们就能以锥形视野比较这些信息将一些不可见的多边形截掉(屏幕 左边和右边的东东)--如果它们不相交,这样你就不必搜索整个子树.DOOM 就是这样做的,在一个巨大的 BSP 树中用特定空间储存了每一级的完整 (*entire*)信息.
下面是搜索 BSP 树的伪代码.函数 left() 当第二个输入矢量在第一个输 入矢量的左边时返回 TRUE.这就是两个矢量的点积,... ... Sorry,小D这一句不太明白 >This is a simple dot product, and by pre-calculating the slope of the >nodeline can be done with one multiply and one subtract.
vector player ; player's map position ; 玩家在地图上的位置矢量 vector left_sightline ; vector representing a ray cast through ; the left-most pixel of the screen ; 描述发射到屏幕最左边的光线的矢量 vector right_sightline ; the right-most pixel of the screen ; 描述发射到屏幕最右边的光线的矢量
structure node { vector vertex1 vector vertex2 node left_subtree node right_subtree face left_face face right_face box bounding_box bool terminal_node face terminal_node_faces[lots] }
recurse(node input)
if (cone defined by left and right sightlines does not intersect the node's bounding box) return fi
if node.terminal_node ; terminal node - add faces to list ; 叶节点--将平面填入表中 add(node.terminal_node_faces) return fi
if left(vertex2-vertex1,player-vertex1) ; player is to the left of the nodeline ; 玩家在节点线的左边 if not left(vertex2-vertex1,right_sightline) ; sight points right - we are looking at the face ; 视线指向右边--我们正看着这个面 recurse(node.right_subtree) add(node.left_face) fi ; now go down the left subtree ; 现在向左子树搜索 recurse(node.left_subtree) else ; player is to the right of the nodeline ; 玩家在节点线的右边 if left(vertex2-vertex1,left_sightline) ; sight points left - we are looking at the face ; 视线指向左边--我们正看着这个面 recurse(node.left_subtree) add(node.right_face) fi ; now go down the right subtree ; 现在向右子树搜索 recurse(node.right_subtree) fi
return
end recurse
这不是正规的代码--象数据结构之类的很多东东都没写. :) |  |
|