zoukankan      html  css  js  c++  java
  • 跨越OpenGL和D3D的鸿沟(二):现代OpenGL

    转载请注明出处为KlayGE游戏引擎,本文地址为http://www.klayge.org/2011/07/17/%e8%b7%a8%e8%b6%8aopengl%e5%92%8cd3d%e7%9a%84%e9%b8%bf%e6%b2%9f%ef%bc%88%e4%ba%8c%ef%bc%89%ef%bc%9a%e7%8e%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 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。它很容易使用:

    // 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[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矩阵,只需要:

    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来调整。简而言之,就是:

    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的功能异同,以及直接相互访问的可能性。

  • 相关阅读:
    (Step by Step)How to setup IP Phone Server(VoIP Server) for free.
    感冒流鼻涕怎么办?
    Robocopy是微软Windows Server 2003资源工具包中众多多用途的实用程序之一(它是基于强大的拷贝程序
    jsp页面:js方法里嵌套java代码(是操作数据库的),如果这个js 方法没被调用,当jsp页面被解析的时候,不管这个js方法有没有被调用这段java代码都会被执行?
    如何在 js 代码中使用 jsp 标签或 Java 代码
    第三范式
    Cannot forward after response has been committed 错误
    安装visio 2010:您的计算机上的Office 2003安装已损坏,安装程序无法继续。请删除或修复office 2003产品并重新运行安装程序
    自己WIN7旗舰版安装 SQLServer2005/2008的一些总结
    建筑基坑工程设计计算与施工(一)
  • 原文地址:https://www.cnblogs.com/gongminmin/p/2109495.html
Copyright © 2011-2022 走看看