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

时间:2022-06-01 16:20:49

I'm writing a program that draws a rotating cube (with texture) in the middle of the screen followed by a small yellow sphere that orbits around the cube. The idea is to make the sphere as a spot light source that illuminates the cube.

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

Here is the problem: as you can see in the images below, I'm failing to achieve the spot light effect. It seems that the entire cube gets lighted:

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

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

I'm setting GL_SPOT_DIRECTION to be the cube position. I didn't set surface normals because I'm struggling to understand how to compute them for the cube, and I'm not sure a simple graphic application like this really requires it.

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

I'm sharing the code below:

我正在分享以下代码:

main.cpp:

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:

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:

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:

Lighting.pro:

QT += core gui opengl

SOURCES += \
    GLWidget.cpp \
    main.cpp

HEADERS += \
    GLWidget.h

RESOURCES += \
    resource.qrc

What needs to be changed in this application in order to achieve the desired effect?

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

1 个解决方案

#1


3  

You do not specify any normals for your cube faces. As OpenGL is a state machine, it will use the default surface normal for all vertices, hence all of your faces. As the normal vector is crucial for the lighting, all of your faces will be lit almost identical (vertex postions still are different, but the effect is weak).

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

You should also be aware that the fixed function lighting of OpenGL is done per vertex. If you really want to see a good spotlight on the cuve, you would either need to tessalate it so more vertices are used where the lighting equation is actually evaluated, or use shaders for per-fragment lighting.

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

#1


3  

You do not specify any normals for your cube faces. As OpenGL is a state machine, it will use the default surface normal for all vertices, hence all of your faces. As the normal vector is crucial for the lighting, all of your faces will be lit almost identical (vertex postions still are different, but the effect is weak).

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

You should also be aware that the fixed function lighting of OpenGL is done per vertex. If you really want to see a good spotlight on the cuve, you would either need to tessalate it so more vertices are used where the lighting equation is actually evaluated, or use shaders for per-fragment lighting.

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