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++ 没有 closures 或 delegates 的概念,这是必需的(出于这个原因,其他语言有)。
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 显得透明的主要内容,如果未能解决你的问题,请参考以下文章