本文主要是介绍跨越opengl和d3d的鸿沟(二):现代opengl,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
原文地址
上一篇提出了跨越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作为顶点颜色输入格式:
1 2 3 | 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 shading
Flat shading在渲染中用的机会远远少于Gouraud shading。很多人只知道Flat shading是选择一个顶点的属性作为primitive上每个像素的属性,而不会注意到D3D和OpenGL在“哪个顶点”上的选择有所区别。D3D 用line或triangle第一个顶点的属性。而OpenGL在line、triangle或quad的时候最后一个顶点的属性(但在polygon的 时候用的是第一个)。
现在,OpenGL出现了GL_EXT_provoking_vertex这个扩展,可以选择使用哪个顶点的属性来驱动(这就是provoking的意思)一个primitive。它很容易使用:
1 2 3 4 5 | // OpenGL原生的方式 glProvokingVertex(GL_LAST_VERTEX_CONVENTION); // D3D的方式 glProvokingVertex(GL_FIRST_VERTEX_CONVENTION); |
该扩展也进入了OpenGL 3.2的核心。
状态切换
D3D9的状态切换是通过SetRenderState这样的函数来完成的,而OpenGL则是完全基于状态机的结构,比如要设置model view矩阵,同时要求不影响状态,就需要:
1 2 3 4 5 6 7 8 9 | void set_model_view_matrix(GLfloat const matrix[16]) { 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矩阵,只需要:
1 2 3 4 | void set_model_view_matrix(GLfloat const matrix[16]) { 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来调整。简而言之,就是:
1 2 3 | 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的声明里添加个属性:
1 2 3 4 5 6 7 8 | // 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的功能异同,以及直接相互访问的可能性。
这篇关于跨越opengl和d3d的鸿沟(二):现代opengl的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!