轨道球应该在中心立方体上投射聚光灯

时间:2012-10-08 23:36:04

标签: c++ qt opengl qglwidget

我正在编写一个程序,在屏幕中间绘制旋转立方体(带纹理),然后是围绕立方体旋转的小黄色球体。我们的想法是将球体作为照亮立方体的聚光源。

这是问题:正如您在下面的图片中看到的那样,我无法实现聚光灯效果。似乎整个立方体都被点亮了:

我将GL_SPOT_DIRECTION设置为立方体位置。我没有设置表面法线,因为我很难理解如何为立方体计算它们,我不确定这样的简单图形应用程序是否真的需要它。

我正在分享以下代码:

的main.cpp

#include <QApplication>
#include "glwidget.h"

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);

    GLWidget gl_widget;
    gl_widget.show();

    return app.exec();
}

GLWidget.h

#pragma once
#include <QGLWidget>
#include <QImage>

class GLWidget : public QGLWidget
{
    Q_OBJECT
public:
    explicit GLWidget(QWidget* parent = 0);
    virtual ~GLWidget();

    void _draw_texture_cube(int w, int h);
    void _draw_light();

    /* OpenGL initialization, viewport resizing, and painting */

    void initializeGL();
    void paintGL();
    void resizeGL( int width, int height);

    /* enable the user to interact directly with the scene using the keyboard */

    void keyPressEvent(QKeyEvent *e);

private:
    int _width;
    int _height;
    QImage* _img;
    GLuint  _texture;
    float xrot;
    float yrot;
    float zrot;
    bool _light_on;
    bool _must_rotate;
    bool _pause_light;
    GLfloat _light_pos[3];
    GLfloat _cube_pos[3];
    GLUquadricObj* _quadratic;

protected slots:
    void _tick();
};

GLWidget.cpp

#include "GLWidget.h"

#include <iostream>
#include <QKeyEvent>
#include <QTimer>

#include <cmath>

#define LIGHT_MOVEMENT_SPEED    20.0f           // Degrees per second
#define pi                      3.141592654f

GLWidget::GLWidget(QWidget *parent)
: QGLWidget(parent), _img(NULL), _light_on(true), _must_rotate(true),
  _pause_light(false), _quadratic(NULL)
{
    _width = 0;
    _height = 0;
    _texture = 0;

    xrot = 0.f;
    yrot = 0.f;
    zrot = 0.f;

    // Set central cube position
    _cube_pos[0] = 0.0f;
    _cube_pos[1] = 0.0f;
    _cube_pos[2] = -7.0f;

    // Set light position
    _light_pos[0] = 0.5f;
    _light_pos[1] = 0.5f;
    _light_pos[2] = -7.0f;
}

GLWidget::~GLWidget()
{
    if (_img)
        delete _img;

    glDeleteTextures(1, &_texture);
}

void GLWidget::_tick()
{
    update(); // triggers paintGL()

    QTimer::singleShot(33, this, SLOT(_tick()));
}

void GLWidget::initializeGL()
{
    std::cout << "GLWidget::initializeGL" << std::endl;
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);       // Black Background

    glEnable(GL_CULL_FACE);

    /* Load bitmap */

    glEnable(GL_TEXTURE_RECTANGLE_ARB);
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);

    if (!_img)
    {
        std::cout << "GLWidget::paintGL: loading image" << std::endl;

        QImage tmp(":/crate.jpg");
        if (tmp.isNull())
        {
            std::cout << "GLWidget::paintGL: !!! Failed QImage #1" << std::endl;
            return;
        }

        _img = new QImage(QGLWidget::convertToGLFormat(tmp));
    }

    /* Convert bitmap into texture */

    // Create The Texture
    glGenTextures(1, &_texture);

    // Typical Texture Generation Using Data From The Bitmap
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture);

    // Generate The Texture
    glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0,
                 GL_RGBA, _img->width(), _img->height(), 0,
                 GL_RGBA, GL_UNSIGNED_BYTE, _img->bits());

    if (glGetError() != GL_NO_ERROR)
    {
        std::cout << "GLWidget::paintGL: !!! Failed glTexImage2D" << std::endl;
        return;
    }

    /* Setup lighting */

    glShadeModel(GL_SMOOTH);    //Smooth color shading

    // Light properties
    GLfloat AmbientLight[4]      = {0.2, 0.2, 0.2, 1.0};
    GLfloat DiffuseLight[4]      = {0.8, 0.8, 0.8, 1.0};      // color
    GLfloat SpecularLight[4]     = {1.0, 1.0, 1.0, 1.0};      // bright
    GLfloat SpecRef[]            = {0.7f, 0.7f, 0.7f, 1.0f};
    GLubyte Shine                = 60.0;

    //glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, AmbientLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientLight);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
    glLightfv(GL_LIGHT0, GL_SPECULAR, SpecularLight);
    glLightfv(GL_LIGHT0, GL_POSITION, _light_pos);

    glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
    glMaterialfv(GL_FRONT, GL_SPECULAR, SpecRef);           // refletância do material
    glMaterialf(GL_FRONT, GL_SHININESS, Shine);             // concentração do brilho
    glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
    //glColorMaterial(GL_FRONT,GL_DIFFUSE);

    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);

    // Sphere
    _quadratic = gluNewQuadric();               // Create A Pointer To The Quadric Object
    gluQuadricNormals(_quadratic, GLU_SMOOTH);  // Create Smooth Normals
    gluQuadricTexture(_quadratic, GL_TRUE);     // Create Texture Coords

    /* Start the timer */

    _tick();
}

/* Draw the central cube with texture
 */
void GLWidget::_draw_texture_cube(int w, int h)
{
    glPushMatrix();

    glTranslatef(_cube_pos[0], _cube_pos[1], _cube_pos[2]);
    glRotatef ( xrot, 1.0, 0.0, 0.0 );
    glRotatef ( yrot, 0.0, 1.0, 0.0 );
    glRotatef ( zrot, 0.0, 0.0, 1.0 );

    glColor3f(1.0f, 1.0f, 1.0f);

    glBegin(GL_QUADS);  // Draw A Cube

        // Front Face
        glTexCoord2f(0.0f, 0.0f);           glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(w, 0.0f);              glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(w, h);                 glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, h);              glVertex3f(-1.0f,  1.0f,  1.0f);

        // Back Face
        glTexCoord2f(w, 0.0f);              glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(w, h);                 glVertex3f(-1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, h);              glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f);           glVertex3f( 1.0f, -1.0f, -1.0f);

        // Top Face
        glTexCoord2f(0.0f, h);              glVertex3f(-1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f);           glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(w, 0.0f);              glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(w, h);                 glVertex3f( 1.0f,  1.0f, -1.0f);

        // Bottom Face
        glTexCoord2f(w, h);                 glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(0.0f, h);              glVertex3f( 1.0f, -1.0f, -1.0f);
        glTexCoord2f(0.0f, 0.0f);           glVertex3f( 1.0f, -1.0f,  1.0f);
        glTexCoord2f(w, 0.0f);              glVertex3f(-1.0f, -1.0f,  1.0f);

        // Right face
        glTexCoord2f(w, 0.0f);              glVertex3f( 1.0f, -1.0f, -1.0f);
        glTexCoord2f(w, h);                 glVertex3f( 1.0f,  1.0f, -1.0f);
        glTexCoord2f(0.0f, _img->height()); glVertex3f( 1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, 0.0f);           glVertex3f( 1.0f, -1.0f,  1.0f);

        // Left Face
        glTexCoord2f(0.0f, 0.0f);           glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(w, 0.0f);              glVertex3f(-1.0f, -1.0f,  1.0f);
        glTexCoord2f(w, h);                 glVertex3f(-1.0f,  1.0f,  1.0f);
        glTexCoord2f(0.0f, h);              glVertex3f(-1.0f,  1.0f, -1.0f);

    glEnd();
    glPopMatrix();

    if (_must_rotate)
    {
        xrot += 0.6f;
        yrot += 0.4f;
        zrot += 0.8f;
    }
}

/* Draw light source and light model (sphere)
 */
void GLWidget::_draw_light()
{
    if (_light_on)
    {
        glEnable(GL_LIGHT0);    // enable lights that we use
    }
    else
    {
        glDisable(GL_LIGHT0);
    }

    static float light_angle = 25.0f;
    if (!_pause_light)          // stop moving the light source
    {
        light_angle += LIGHT_MOVEMENT_SPEED * 0.1;
        if (light_angle > 360.0f)
            light_angle -= 360.0f;
    }

    /* Set light source position */

    _light_pos[0] = 4.0f * (float) cos(light_angle * pi / 180.0f);
    _light_pos[1] = 4.0f * (float) sin(light_angle * pi / 180.0f);
    _light_pos[2] = -7;
    glLightfv(GL_LIGHT0, GL_POSITION, _light_pos);

    GLfloat SpotDir[] = {_cube_pos[0], _cube_pos[1], _cube_pos[2], 0.0 };
    glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, SpotDir);

    glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 150.0);
    glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 15.0);

    /* Set the light model position to be the same as the light source */

    glPushMatrix();
        glTranslatef(_light_pos[0], _light_pos[1], _light_pos[2]);
        glColor3ub(255, 255, 0);                // yellow
        gluSphere(_quadratic, 0.2f, 32, 32);    // draw sphere
    glPopMatrix();
}

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer

    glMatrixMode   ( GL_MODELVIEW );    // Select The Model View Matrix
    glLoadIdentity();                   // Reset The Current Modelview Matrix

    /* Draw central cube */

    glEnable(GL_TEXTURE_RECTANGLE_ARB);
    glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture);       // Select Our Texture
    _draw_texture_cube(_img->width(), _img->height());
    glDisable(GL_TEXTURE_RECTANGLE_ARB);

    /* Draw light source and light model*/

    _draw_light();
}

void GLWidget::resizeGL( int w, int h)
{
    _width = w;
    _height = h;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);    // Select The Projection Matrix
    glLoadIdentity();               // Reset The Projection Matrix
    if (h == 0)                     // Calculate The Aspect Ratio Of The Window
       gluPerspective ( 60, ( float ) w, 0.4, 500.0 );
    else
       gluPerspective ( 60, ( float ) w / ( float ) h, 0.4, 500.0 );

    glMatrixMode   ( GL_MODELVIEW );  // Select The Model View Matrix
    glLoadIdentity ( );    // Reset The Model View Matrix
    gluLookAt(0.0,  0.0, 2.0,   // eye
              0.0,  0.0, 0.0,   // center
              0.0,  1.0, 0.0);  // up
}

void GLWidget::keyPressEvent(QKeyEvent *e)
{
    switch (e->key())
    {
        case Qt::Key_L:
            if (_light_on)
                _light_on = false;
            else
                _light_on = true;
        break;

        case Qt::Key_P:
            if (_pause_light)
                _pause_light = false;
            else
                _pause_light = true;
        break;

        case Qt::Key_R:
            if (_must_rotate)
               _must_rotate = false;
            else
               _must_rotate = true;
        break;

        default:
        break;
    }
}

Lighting.pro

QT += core gui opengl

SOURCES += \
    GLWidget.cpp \
    main.cpp

HEADERS += \
    GLWidget.h

RESOURCES += \
    resource.qrc

为了达到预期的效果,需要在此应用程序中进行哪些更改?

1 个答案:

答案 0 :(得分:3)

您没有为立方体面指定任何法线。由于OpenGL是状态机,它将使用所有顶点的默认曲面法线,因此所有面都使用。由于法线向量对于光照是至关重要的,因此所有的面都会被点亮几乎相同(顶点位置仍然不同,但效果很弱)。

您还应该知道OpenGL的固定功能照明是按顶点完成的。如果你真的想在cuve上看到一个好的聚光灯,你可能需要对它进行讨论,以便在实际评估光照方程的地方使用更多的顶点,或者为每个片段的光照使用着色器。