1

使用 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;
}
4

2 回答 2

12

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

#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如果是 class 的成员函数GAME,除非这是一个静态成员函数,否则不能将类成员函数用作回调,尤其是对于不知道类的 C 库。它应该如何知道该事件函数属于哪个实例?C++ 没有闭包委托的概念,这是必需的(出于这个原因,其他语言有)。

    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);

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

更新但是下一行是错误的:

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

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

    glMatrixMode(GL_MODELVIEW);

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

您实际上应该设置模型视图矩阵,并在此处从头开始完成所有转换。

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

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

然而,在你开始渲染之前,你应该,acutually必须设置所有需要的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该类管理所有实例的静态列表并提供静态成员函数,将事件传递给列表中的所有实例。或者使用单例(但是我认为单例是一种反模式,应该避免)。

于 2011-10-06T19:04:31.003 回答
2

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

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

其次,您没有绘制顶面,您有两个底面(您忘记为顶部设置 y=1)。

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

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

于 2011-10-06T18:25:49.010 回答