array 发表于 2011-7-21 13:37:09

转:跨越OpenGL和D3D的鸿沟(二):现代OpenGL

非常好的讲述OpenGL与D3D的系列文章,转载到论坛与大家分享。原文刊载地址:KlayGE游戏引擎,转载请注明该网址以及原作者gongminmin。原文地址:http://www.klayge.org/2011/07/17 ... %b0%e4%bb%a3opengl/


上一篇提出了跨越OpenGL和D3D的基本问题,介绍了一些能在不改变API的情况下,通过输入数据来消除OpenGL和D3D之区别。本篇的重点是如何利用现代OpenGL提供的扩展和新功能,消除一些无法在上层解决的问题。顶点颜色顺序D3D9 最常用的顶点颜色格式是BGRA格式(也就是D3DCOLOR),而OpenGL默认用的是RGBA格式。D3D9用BGRA纯粹是因为历史原因,早期硬 件不支持UBYTE4的格式,只能用D3DCOLOR,然后再shader里调用D3DCOLORtoUBYTE4。现在的GPU都支持 UBYTE4,D3D10+也是可以直接使用RGBA,所以这已经不是问题了。如果需要兼容已经生成BGRA格式数据,现代OpenGL提供了GL_EXT_vertex_array_bgra这个扩展,也可以使用BGRA作为顶点颜色输入格式:glColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, stride, pointer);
glSecondaryColorPointer(GL_BGRA, GL_UNSIGNED_BYTE, stride, pointer);
glVertexAttribPointer(GL_BGRA, GL_UNSIGNED_BYTE, stride, pointer);
该扩展进入了OpenGL 3.2的核心。Flat shadingFlat shading在渲染中用的机会远远少于Gouraud shading。很多人只知道Flat shading是选择一个顶点的属性作为primitive上每个像素的属性,而不会注意到D3D和OpenGL在“哪个顶点”上的选择有所区别。D3D 用line或triangle第一个顶点的属性。而OpenGL在line、triangle或quad的时候最后一个顶点的属性(但在polygon的 时候用的是第一个)。现在,OpenGL出现了GL_EXT_provoking_vertex这个扩展,可以选择使用哪个顶点的属性来驱动(这就是provoking的意思)一个primitive。它很容易使用:// OpenGL原生的方式
glProvokingVertex(GL_LAST_VERTEX_CONVENTION);


// D3D的方式
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
该扩展也进入了OpenGL 3.2的核心。状态切换D3D9的状态切换是通过SetRenderState这样的函数来完成的,而OpenGL则是完全基于状态机的结构,比如要设置model view矩阵,同时要求不影响状态,就需要:void set_model_view_matrix(GLfloat const matrix)
{
   GLenum saved_mode;
   glGetIntegerv(GL_MATRIX_MODE, &saved_mode);
   glMatrixMode(GL_MODELVIEW);
   glLoadMatrixf(matrix);
   glMatrixMode(saved_mode);
}
如果这里不这样繁琐,就很可能在十万八千里的地方出问题。相信每个用OpenGL的人都曾遇到过,尤其是多人合作的时候。一个状态的错误都可能导致灾难。现在,救星来了。GL_EXT_direct_state_access扩展(简称DSA)的出现大大地改变了这点。该扩展提供了直接访问状态的能力,比如前面的设置model view矩阵,只需要:void set_model_view_matrix(GLfloat const matrix)
{
   glMatrixLoadfEXT(GL_MODELVIEW, matrix);
}
简单多了吧。DSA把绝大部分OpenGL核心和各个扩展提供的状态都增加了一个直接访问的版本,相当方便。理论上,性能还能有所提高。可惜的是,DSA至今还没进入OpenGL的核心,虽然在NV和ATI的卡上都可以使用。窗口原点上一篇提到了坐标系的区别,另一个类似的区别出现在窗口朝向上。D3D用了左上角作为原点,而OpenGL用了左下角。D3D9用了像素左上角作为原点,而OpenGL和D3D10+用了像素中心。在像素和纹理需要1:1对应的时候,该问题就需要严重关注了。详见Directly Mapping Texels to Pixels。窗口原点的不同造成的结果就是,两个API做render to texture之后,产生的texture在y方向是相反的。这本身可以通过调整project matrix来调整。简而言之,就是:glMatrixLoadIdentityEXT(GL_PROJECTION);
glMatrixScalefEXT(GL_PROJECTION, 1, -1, 0); // y方向取反
glMatrixTranslatefEXT(GL_PROJECTION,0.5f / win_width, 0.5f / win_height, 0); // 调整到D3D9的话还需要偏移0.5个像素
由于y方向反了,还需要调用glFrontFace(GL_CW)来把正面方向反一下,否则cull会出错。窗口原点就这样通过上层代码来解决了。但这只能调整窗口原点,OpenGL下像素坐标仍是以左下角作为原点,而像素坐标在post process里很常用。因此,OpenGL提供了GL_ARB_fragment_coord_conventions这个扩展,专门用来指定像素坐标的原点和偏移。在GLSL的声明里添加个属性:// OpenGL原生的方式
in vec4 gl_FragCoord;


// D3D9的方式
layout(origin_upper_left, pixel_center_integer) in vec4 gl_FragCoord;


// D3D10+的方式
layout(origin_upper_left) in vec4 gl_FragCoord;
这样就可以把像素坐标调整过来。综上所述,通过现代OpenGL核心和扩展的支持,填平了一些原本被认为位于底层的区别,同时不会有性能损失。破解了上篇提到的流言3。下篇将剖析两个API的功能异同,以及直接相互访问的可能性。

sky11811 发表于 2011-7-21 14:46:31

opengl的状态机制确实是个问题,多人开发时尤为明显,但是在你使用某些状态时可以首先查询该功能是否被开启或者处于什么状态,说白了小心点也是不会有问题的。

w910916 发表于 2012-10-26 11:09:01

array大哥 我老崇拜你了真想什么时候你开个免费的网上讲课 我肯定期期都准时看 :'({:3_51:}

zuohuaijun 发表于 2012-11-21 10:10:58

不错!!!又张见识啦!!!
页: [1]
查看完整版本: 转:跨越OpenGL和D3D的鸿沟(二):现代OpenGL