OpenGL/GLFW看起来是透明的
OpenGL / GLFW appear transparent
使用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;
}
很抱歉告诉您,您的代码在几个级别上都被破坏了。让我为你分解一下:
#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++没有闭包或委托的概念,这是必需的(其他语言也有,正是因为这个原因(。
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
是指在模型视图矩阵上执行的函数。modelview矩阵负责将模型放置在世界空间中,并将世界与视图对齐,因此转换为model→world, world→view
,您可以减少中间的world
步骤,因此它仅为model→view
。
glMatrixMode(GL_MODELVIEW);
在这里您可以拨打glLoadIdentity(); gluLookAt(...);
。现在应该很清楚了,为什么在事件处理程序中进行矩阵操作毫无意义。
实际上,您应该设置modelview矩阵,所有转换都是从头开始的。
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
类管理所有实例的静态列表,并提供一个静态成员函数,该函数将事件传递给列表中的所有实例。或者使用singleton(但是我认为singleton是一种反模式,应该避免(。
虽然我不确定问题是什么(它们看起来真的透明吗?(,但代码中显而易见的第一点是,您启用了照明,但没有为顶点指定任何法线向量。需要这些来告诉OpenGL矢量面向哪个方向(对象的实际曲面是如何定向的,但在每个离散顶点(。
但在立方体的情况下,每个面(四边形(都应该有一个法线向量(意味着四边形的每个顶点都应该有相同的法线(。因此,在绘制面的四个顶点之前,只需指定一个适当的法线向量(类似于指定颜色的方式(。在您的情况下,前面应该是(0,0,1(,左边应该是(1,0,0(,后面应该是(,0,-1(,右边应该是(-1.0,0(,顶部应该是(0,1,0(和底部应该是(0,-1,0(。
第二,你没有画一个顶面,你有两个底面(你忘了为顶部设置y=1(。
第三,很好的做法是一致地排列对象面的顶点,这样当从外部观察时,所有面的方向都是逆时针或顺时针的(实际上,你可以通过这个方向定义外部是什么(,但不是一些这样,另一些那样。尽管在您当前的配置中,这不会造成任何危害,但一旦启用背面剔除(一种非常常见且易于使用的优化技术(或尝试自动计算面法线,您就会遇到问题。
如果这对你来说都是疯狂的言论,那么就深入研究OpenGL和计算机图形学吧。
- std::vector的包装器,使数组的结构看起来像结构的数组
- 看起来is_nothrow_constructible_v()在MSVC中被破坏了,我错了吗
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 尽管一切看起来都很好,但值不会交换
- 自制的上衣:看起来一样,但不完全相同
- 如何使它看起来像正在下的雪
- 如何用逗号分隔输出?如何改进此代码以使其看起来更体面?
- 我的代码看起来不错,但某些输入会导致不需要的输出
- 为什么这个结构的大小是 40,而它看起来应该是 12/24?
- 我需要将阵列样式的邻接矩阵转换为矢量样式(以使其看起来更好)
- 结构化绑定:当某些内容看起来像引用并且行为类似于引用,但它不是引用时
- 如何使QTextEdit看起来被禁用
- 在正确性或良好的代码结构方面,这种动态对象创建看起来如何
- 看起来如此主要的错误.cpp:(.text.startup+0xd6):未定义对"vtable for Counter"的引用?
- C 时间测量看起来太慢了
- 我应该如何使 std::filesystem 看起来符合 Visual Studio 2015 的标准
- 写入.ini文件 - SimpleIni SetValue 尽管看起来成功了,但什么也没做
- std::string 上的 substr 无法正常工作,因为存在一些不可见但看起来像空格的字符
- DirectX透明纹理-使植物看起来很好看
- OpenGL/GLFW看起来是透明的