OpenGL / GLFW 显得透明

Posted

技术标签:

【中文标题】OpenGL / GLFW 显得透明【英文标题】:OpenGL / GLFW appear transparent 【发布时间】:2011-10-06 18:02:59 【问题描述】:

使用 opengl 和 glfw 绘制一个简单的立方体时,立方体的面看起来是透明的。 这是代码。使用箭头键旋转。我刚刚在我的程序中封装了一个类。 使用 Visual C++ Ultimate 2010。

#include "GAME.h"
using namespace std;
GAME::GAME() 

    glfwInit();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING); 
    glEnable(GL_LIGHT0); 
    glEnable(GL_LIGHT1); 
    glEnable(GL_NORMALIZE); 

int GAME::execute() 

    glfwOpenWindow(640, 320, 16, 16, 16, 16, 16, 16, GLFW_WINDOW);
    glfwSetWindowTitle("Viraj");
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glfwSetKeyCallback(events);
    running = true;
    while(glfwGetWindowParam(GLFW_OPENED))
    
        glfwPollEvents();
        loop();
        render();
    
    return 0;

void GAME::events(int key, int action)

    switch(key)
    
    case GLFW_KEY_UP:
        glRotatef(10, 1, 0, 0);
        break;
    case GLFW_KEY_DOWN:
        glRotatef(-10, 1, 0, 0);
        break;
    case GLFW_KEY_RIGHT:
        glRotatef(10, 0, 1, 0);
        break;
    case GLFW_KEY_LEFT:
        glRotatef(-10, 0, 1, 0);
        break;
    

int GAME::loop()

    return 0;

int GAME::render()

    int win_width;
    int win_height;
    glfwGetWindowSize(&win_width, &win_height);
    const float win_aspect = (float)win_width / (float)win_height;
    glViewport(0, 0, win_width, win_height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //glOrtho(-win_aspect, win_aspect, -1., 1., -1., 1.);
    gluPerspective(90, win_aspect, 1, 100.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0, 0, 3.0, 0, 0, 0, 0.0, 1.0, 0.0);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING); 
    glEnable(GL_LIGHT0); 
    glEnable(GL_LIGHT1); 
    glEnable(GL_NORMALIZE); 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glBegin(GL_QUADS);
    glRotatef(-1, 0, 1, 0);
    glColor3f(0.0f, 0.0f, 0.0f);
    //Front
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);
    glVertex3f(1.0, 1.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);

    glColor3f(1.0f, 0.0f, 0.0f);
    //Left
    glVertex3f(1.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(1.0, 1.0, 0.0);

    glColor3f(0.0f, 1.0f, 0.0f);
    //Back
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(0.0, 1.0, -1.0);
    glVertex3f(0.0, 0.0, -1.0);

    glColor3f(0.0f, 0.0f, 1.0f);
    //Right
    glVertex3f(0.0, 0.0, -1.0);
    glVertex3f(0.0, 1.0, -1.0);
    glVertex3f(0.0, 1.0, 0.0);
    glVertex3f(0.0, 0.0, 0.0);

    glColor3f(1.0f, 0.0f, 1.0f);
    //Top
    glVertex3f(0.0, 1.0, -0.0);
    glVertex3f(0.0, 1.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(1.0, 1.0, 0.0);

    glColor3f(1.0f, 1.0f, 0.0f);
    //Bottom
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(0.0, 0.0, -1.0);

    glEnd();
    glfwSwapBuffers();
    return 0;

【问题讨论】:

有什么问题?它们是否看起来透明而您不希望它们(确实很奇怪)?或者你想让它们看起来透明而你不知道怎么做(措辞会有点奇怪)? 【参考方案1】:

很遗憾地告诉你,你的代码在几个层面上都被破坏了。让我为你分解一下:

#include "GAME.h"
using namespace std;
GAME::GAME() 

    glfwInit();
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING); 
    glEnable(GL_LIGHT0); 
    glEnable(GL_LIGHT1); 
    glEnable(GL_NORMALIZE); 

这是第一个错误:GLFW 是一个 C 库,需要准确地初始化一次。对glfwInit() 的调用属于主函数,而不是类构造函数。其他函数调用是 OpenGL 调用,但是它们需要一个活动的 OpenGL 上下文。但是,此时程序没有 OpenGL 上下文,因此您所做的所有调用都没有效果。

int GAME::execute() 

    glfwOpenWindow(640, 320, 16, 16, 16, 16, 16, 16, GLFW_WINDOW);
    glfwSetWindowTitle("Viraj");
    glClearColor(1.0, 1.0, 1.0, 1.0);

再次强调,GLFW 的本质并不适合作为课程的一部分使用。 GLFW 中只能有一个窗口,并且只有一个事件循环。这不能很好地映射到类和对象。你当然可以有一个 EventLoop 或类似的课程,但你不会那样使用它。

然后是下一行,我很惊讶它居然编译了:

    glfwSetKeyCallback(events);

events 如果是类GAME 的成员函数,除非这是一个静态成员函数,否则不能使用类成员函数作为回调,尤其是不知道类的C 库。它应该如何知道该事件函数属于哪个实例? C++ 没有 closuresdelegates 的概念,这是必需的(出于这个原因,其他语言有)。

    running = true;
    while(glfwGetWindowParam(GLFW_OPENED))
    
        glfwPollEvents();
        loop();
        render();
    
    return 0;

现在出现的是一个经典的新手误解:

void GAME::events(int key, int action)

    switch(key)
    
    case GLFW_KEY_UP:
        glRotatef(10, 1, 0, 0);
        break;
    case GLFW_KEY_DOWN:
        glRotatef(-10, 1, 0, 0);
        break;
    case GLFW_KEY_RIGHT:
        glRotatef(10, 0, 1, 0);
        break;
    case GLFW_KEY_LEFT:
        glRotatef(-10, 0, 1, 0);
        break;
    

矩阵操作调用只对绘图代码有意义。在这里调用 glRotate 只会弄乱矩阵堆栈,但是每个理智的 OpenGL 渲染函数都会初始化所有状态,因此在开始时会有一些理智的值。

您希望在事件处理程序中执行的操作是将所有输入累积到变量中,稍后在绘图代码中使用这些变量来设置和控制渲染。

int GAME::loop()

    return 0;

如果这意味着循环,为什么没有循环?

int GAME::render()

    int win_width;
    int win_height;
    glfwGetWindowSize(&win_width, &win_height);
    const float win_aspect = (float)win_width / (float)win_height;

只缺少一个细节:您需要在此处设置视口。没什么大不了的:glViewport(0, 0, win_width, win_height);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    //glOrtho(-win_aspect, win_aspect, -1., 1., -1., 1.);
    gluPerspective(90, win_aspect, 1, 100.0);

现在这实际上很好! 您确实在渲染函数中设置了投影矩阵。一路走好,坚持这个模式!

UPDATE但是下一行是错误的:

    gluLookAt(0, 0, 3.0, 0, 0, 0, 0.0, 1.0, 0.0);

gluLookAt 是要在 modelview 矩阵上执行的函数。模型视图矩阵负责将模型放置在世界空间中并将世界与视图对齐,因此转换为model→world, world→view,您可以减少中间的world 步骤,因此它只是model→view

    glMatrixMode(GL_MODELVIEW);

您可以在这里致电glLoadIdentity(); gluLookAt(...);。现在应该很明显了,为什么在事件处理程序中进行矩阵操作是没有意义的。

您实际上应该设置模型视图矩阵,所有转换都在这里从头开始。

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

在设置好矩阵、视口等之后调用glClear有点不寻常,但并没有错。你可以这样。

但是,在开始渲染之前,您应该必须设置所有所需的 OpenGL 状态。请记住构造函数中的那些“初始化”OpenGL 调用。他们属于这里

    glBegin(GL_QUADS);

    glColor3f(0.0f, 0.0f, 0.0f);
    //Front
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);
    glVertex3f(1.0, 1.0, 0.0);
    glVertex3f(0.0, 1.0, 0.0);

    glColor3f(1.0f, 0.0f, 0.0f);
    //Left
    glVertex3f(1.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(1.0, 1.0, 0.0);

    glColor3f(0.0f, 1.0f, 0.0f);
    //Back
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(1.0, 1.0, -1.0);
    glVertex3f(0.0, 1.0, -1.0);
    glVertex3f(0.0, 0.0, -1.0);

    glColor3f(0.0f, 0.0f, 1.0f);
    //Right
    glVertex3f(0.0, 0.0, -1.0);
    glVertex3f(0.0, 1.0, -1.0);
    glVertex3f(0.0, 1.0, 0.0);
    glVertex3f(0.0, 0.0, 0.0);

    glColor3f(1.0f, 0.0f, 1.0f);
    //Top
    glVertex3f(0.0, 0.0, -0.0);
    glVertex3f(0.0, 0.0, -1.0);
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(1.0, 0.0, 0.0);

    glColor3f(1.0f, 1.0f, 0.0f);
    //Bottom
    glVertex3f(0.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, 0.0);
    glVertex3f(1.0, 0.0, -1.0);
    glVertex3f(0.0, 0.0, -1.0);

    glEnd();

如果您想要照明,则需要提供法线。但是我不会费心添加一大堆glNormal 调用那里:立即模式(glBegin,glEnd)已经过时,我强烈建议您了解顶点数组和顶点缓冲区对象。

    glfwSwapBuffers();
    return 0;


总结:将 GLFW 调用从类中取出。 GLFW 不是面向对象的。只需从 main 函数全局使用它。将事件传递给类是有序的,但您不能将类成员函数用作 GLFW 的回调。你需要写一些辅助函数

extern GAME *pGame;
void eventhandler(int key, int action)

    pGame->event(key, action);

您还可以让GAME 类管理所有实例的静态列表并提供静态成员函数,将事件传递给列表中的所有实例。或者使用单例(但是我认为单例是一种反模式,应该避免使用)。

【讨论】:

W-O-W。如果可以的话,我会+5! events 函数是静态的,这就是它编译的原因。我问了一个不同的问题。 编辑了渲染函数的代码。旋转不起作用。是不是因为 gluLookat 重置了眼睛视角? @viraj:确实,就像我解释的那样,你不应该从事件处理程序中执行 glRotate,因为在那里所做的任何事情都会被正确编写的渲染函数重置。将事件的结果存储在一些变量中,使用它们在渲染代码中应用转换。 @viraj:对 C 回调使用静态成员函数不是好的风格——而且会产生误导。你不应该这样做。但这不是一个 OpenGL 问题,而是一个关于使用 C++ 进行良好 OOP 设计的问题。如果 C++ 中有委托,那将是有意义的,但事实并非如此。所以请不要使用它。【参考方案2】:

虽然我不确定问题是什么(它们真的看起来是透明的吗?),但您的代码中明显的第一个地方是,您启用了光照,但您没有为顶点指定任何法线向量。这些是告诉 OpenGL 一个向量所面向的方向(对象的实际表面是如何定向的,但在每个离散顶点处)所需要的。

但在立方体的情况下,每个面(四边形)都应该有一个法线向量(这意味着一个四边形的每个顶点应该有相同的法线)。因此,只需在绘制面的四个顶点之前指定适当的法线向量(类似于指定颜色的方式)。在您的情况下,这些应该是前面的 (0,0,1),左边的 (1,0,0),后面的 (0,0,-1),右边的 (-1,0,0),( 0,1,0) 表示顶部,(0,-1,0) 表示底部。

其次,你没有画一个顶面,你有两个底面(你忘了为顶部设置 y=1)。

第三,良好的做法是对对象面的顶点进行一致的排序,以便从外部看时所有面都以逆时针或顺时针方向定向(实际上,您通过此方向定义外部是什么),但不是有些这样,有些则不是这样。尽管在您当前的配置中这不会造成任何伤害,但一旦启用背面剔除(一种非常常见且易于使用的优化技术)或尝试自动计算面法线,您就会遇到问题。

如果这对你来说都是疯狂的话题,那么请更深入地研究 OpenGL 和计算机图形学。

【讨论】:

+1 表示顶面。顺序有什么问题:前->左->后->右上->底部逆时针出现在我身上! 我不是那个顺序。我的意思是单个面的顶点的顺序。对于所有面(如果从外面看,顺时针或逆时针),这些应始终按相同顺序排列。但无论如何,主要的错误是缺少法线。

以上是关于OpenGL / GLFW 显得透明的主要内容,如果未能解决你的问题,请参考以下文章

透明天空盒+透明高度图的OpenGL混合问题

OpenGL透明与混色效果

透明四边形上的OpenGL DECAL纹理?

体素中的 OpenGL 3D 透明度

OpenGL半透明四边形神器

OpenGL 使纹理透明 VBO