C++ - Qt Framework - Using QOpenGLWidget to display a shape with lighting and normal vectors

Lighting is certainly one of the most interesting effect that we can make with OpenGL.

Combined with colors and normal vectors, lighting becomes visually realistic.

In this tutorial we are going to see how to draw a main shape (kind of a rectangular cube), another one inside the first one and a third representing our lighting source.

We are also going to add normal vectors to these two shapes but with opposite directions.

So let's get started.

First of all

For this tutorial, we'll use the same camera mechanism than from another tutorial:

https://www.badprog.com/c-qt-framework-using-qopenglwidget-to-display-a-window-for-moving-shapes-with-keyboard-and-mouse

To sum up what's going on in this new Qt OpenGL Tutorial:

VAO is the manager for one or many shapes, it says that all the VBOs after the bind() method will belong to it.

Shader files can be initialized for each shape or for many ones.

Each location element in the shader file is linked to the .CPP code with QOpenGLShaderProgram attributeLocation() methods.

Each of these element represents a vector in the StructVertex vectors that we set in the initializeGL() method.

In general we set 2 VBOs, one for the structure of Vectors of each element (position, color, texture and normal) and the other one for an array of indices in order to have a better handling of the first array.

The VBOs data are set after the VBO bind() and before the VBO release() method.

The VAO handles 1 or many VBOs, in our case two for the main shape.

Then we'll use the Qt GUI to handle the lighting position and its colors.

Shaders will be of course used for this tutorial.

To have a better understanding of the code, we have separated the colors from the lighting vertex and fragment shaders.

The dot product will help us to manipulate the coordinate of the lighting

We'll also deal with the ambient and diffuse lighting in order to accentuate the lighting strength.

As you can see, in the video tutorial, the inner shape from the cube is displayed in colors when the light rays are emitted in the opposite direction.

So we can have a shape illuminated when another is darkened and vice versa.

Let's code a bit

BadprogOpenGLLightingWithNormal.pro

QT       += core gui opengl


#badprog.com

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets


CONFIG += c++11


# The following define makes your compiler emit warnings if you use

# any Qt feature that has been marked deprecated (the exact warnings

# depend on your compiler). Please consult the documentation of the

# deprecated API in order to know how to port your code away from it.

DEFINES += QT_DEPRECATED_WARNINGS


# You can also make your code fail to compile if it uses deprecated APIs.

# In order to do so, uncomment the following line.

# You can also select to disable deprecated APIs only up to a certain version of Qt.

#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += \

    badprogOpenglWidget.cpp \

    camera3d.cpp \

    input.cpp \

    main.cpp \

    mainWindow.cpp \

    transform3d.cpp


HEADERS += \

    badprogOpenglWidget.h \

    camera3d.h \

    input.h \

    mainWindow.h \

    transform3d.h


FORMS += \

    mainWindow.ui


RESOURCES += \

    badprog.qrc


# Default rules for deployment.

qnx: target.path = /tmp/$${TARGET}/bin

else: unix:!android: target.path = /opt/$${TARGET}/bin

!isEmpty(target.path): INSTALLS += target
 

main.cpp

#include "mainWindow.h"

#include <QApplication>
// badprog.com

//
int main(int argc, char *argv[]) {
    //
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

 

mainWindow.cpp

#include "mainWindow.h"
#include "badprogOpenglWidget.h"

#include <QKeyEvent>
#include <QDebug>

// badprog.com
// ----------------------------------------------------------------------------
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //
    ui.setupUi(this);

    //
    setWindowTitle("Badprog.com - OpenGL tutorial - Lighting and normal");

    //
    _bpOpenGLWidget = new BadprogOpenGLWidget(this);
    _bpOpenGLWidget->setMinimumWidth(300);
    _bpOpenGLWidget->setMinimumHeight(300);

    //
    ui.verticalLayout->addWidget(_bpOpenGLWidget);

    //
    ui.checkBox->setText("On");
    ui.checkBox->setChecked(true);

    //
    ui.radioButton_color->setChecked(true);
    ui.pushButton_resetCamera->setText("Reset camera position");
    ui.pushButton_resetObject->setText("Reset object position");
    ui.pushButton_resetBoth->setText("Reset both positions");
    ui.label_activateScreenWithMouse->hide();

    //
    ui.label_sliderValueRed->setText("1");
    ui.label_sliderValueGreen->setText("1");
    ui.label_sliderValueBlue->setText("1");
    ui.verticalSlider_red->setValue(100);
    ui.verticalSlider_green->setValue(100);
    ui.verticalSlider_blue->setValue(100);

    //
    ui.label_sliderLightingPositionX->setText("0");
    ui.label_sliderLightingPositionY->setText("0");
    ui.label_sliderLightingPositionZ->setText("0");
    ui.verticalSlider_lightingPositionX->setValue(0);
    ui.verticalSlider_lightingPositionY->setValue(0);
    ui.verticalSlider_lightingPositionZ->setValue(0);

    //
    _switcher = false;

    //
    QObject::connect(ui.pushButton_resetCamera, &QPushButton::clicked, this, &MainWindow::slotResetCamera);
    QObject::connect(ui.pushButton_resetObject, &QPushButton::clicked, this, &MainWindow::slotResetObject);
    QObject::connect(ui.pushButton_resetBoth, &QPushButton::clicked, this, &MainWindow::slotResetBoth);
    QObject::connect(ui.checkBox, &QPushButton::clicked, this, &MainWindow::slotStopStart);
    QObject::connect(ui.radioButton_color, &QRadioButton::clicked, this, &MainWindow::slotRadioButtonSwitchColorTexture);
    QObject::connect(ui.radioButton_texture, &QRadioButton::clicked, this, &MainWindow::slotRadioButtonSwitchColorTexture);

    //
    QObject::connect(ui.verticalSlider_red, &QSlider::valueChanged, this, &MainWindow::slotSliderLightingColorValueChanged);
    QObject::connect(ui.verticalSlider_green, &QSlider::valueChanged, this, &MainWindow::slotSliderLightingColorValueChanged);
    QObject::connect(ui.verticalSlider_blue, &QSlider::valueChanged, this, &MainWindow::slotSliderLightingColorValueChanged);

    //
    QObject::connect(ui.verticalSlider_lightingPositionX, &QSlider::valueChanged, this, &MainWindow::slotSliderLightingPositionValueChanged);
    QObject::connect(ui.verticalSlider_lightingPositionY, &QSlider::valueChanged, this, &MainWindow::slotSliderLightingPositionValueChanged);
    QObject::connect(ui.verticalSlider_lightingPositionZ, &QSlider::valueChanged, this, &MainWindow::slotSliderLightingPositionValueChanged);

    //
    ui.groupBox_3->hide();
}

// ----------------------------------------------------------------------------
MainWindow::~MainWindow() {
//    delete ui;
}

// ----------------------------------------------------------------------------`
void MainWindow::slotSliderLightingPositionValueChanged() {
    //
    QObject *object = sender();

    //
    QSlider *slider = nullptr;
    LIGHTING_POSITION lightingPosition;

    slider = dynamic_cast<QSlider *>(object);

    float valueFromSliderPosition = (float)slider->value() / 5;
    QString sliderValueToDisplay = QString::number(valueFromSliderPosition);

    //
    if (ui.verticalSlider_lightingPositionX== object) {
        ui.label_sliderLightingPositionX->setText(sliderValueToDisplay);
        lightingPosition = LIGHTING_POSITION_X;
    } else if (ui.verticalSlider_lightingPositionY == object) {
        ui.label_sliderLightingPositionY->setText(sliderValueToDisplay);
        lightingPosition = LIGHTING_POSITION_Y;
    } else if (ui.verticalSlider_lightingPositionZ == object) {
        ui.label_sliderLightingPositionZ->setText(sliderValueToDisplay);
        lightingPosition = LIGHTING_POSITION_Z;
    }

    //
    _bpOpenGLWidget->updateLightingPosition(lightingPosition, sliderValueToDisplay);
}

// ----------------------------------------------------------------------------`
void MainWindow::slotSliderLightingColorValueChanged() {
    //
    QObject *object = sender();
    int result = -1;

    //
    QSlider *slider = nullptr;
    LIGHTING_COLOR lightingColor;

    slider = dynamic_cast<QSlider *>(object);

    float valueFromSlider = (float)slider->value() / 100;
    QString sliderValueToDisplay = QString::number(valueFromSlider);

    //
    if (ui.verticalSlider_red == object) {
        ui.label_sliderValueRed->setText(sliderValueToDisplay);
        lightingColor = LIGHTING_COLOR_RED;
    } else if (ui.verticalSlider_green == object) {
        ui.label_sliderValueGreen->setText(sliderValueToDisplay);
        lightingColor = LIGHTING_COLOR_GREEN;
    } else if (ui.verticalSlider_blue == object) {
        ui.label_sliderValueBlue->setText(sliderValueToDisplay);
        lightingColor = LIGHTING_COLOR_BLUE;
    }

    //
    _bpOpenGLWidget->updateLightingColor(lightingColor, sliderValueToDisplay);
}

// ----------------------------------------------------------------------------`
void MainWindow::slotRadioButtonSwitchColorTexture() {
    //
    QObject *object = sender();
    int result = -1;

    //
    if (ui.radioButton_color == object) {
        result = APPAREANCE_TYPE::APPAREANCE_TYPE_COLOR;
    } else if (ui.radioButton_texture == object) {
        result = APPAREANCE_TYPE::APPAREANCE_TYPE_TEXTURE;
    }

    //
    _bpOpenGLWidget->retrieveAppearence(result);
}

// ----------------------------------------------------------------------------
void MainWindow::slotResetCamera() {
    //
    if (_bpOpenGLWidget) {
        _bpOpenGLWidget->resetCamera();
    }
}

// ----------------------------------------------------------------------------
void MainWindow::slotResetObject() {
    //
    if (_bpOpenGLWidget) {
        _bpOpenGLWidget->resetObject();
    }
}

// ----------------------------------------------------------------------------
void MainWindow::slotResetBoth() {
    //
    if (_bpOpenGLWidget) {
        _bpOpenGLWidget->resetBoth();
    }
}

// ----------------------------------------------------------------------------
void MainWindow::slotActivateOpenGLScreen() {
    //
    ui.label_activateScreenWithMouse->hide();
}

// ----------------------------------------------------------------------------
void MainWindow::slotStopStart() {
    //
    bool result = _bpOpenGLWidget->changeTimerState();

    //
    QString textToDisplay = result ? "On" : "Off";

    //
    ui.checkBox->setText(textToDisplay);

    //
    _bpOpenGLWidget->rotateSwitcher(ui.checkBox->isChecked());
}

 

mainWindow.h

#ifndef BADPROG_MAIN_WINDOW_H
#define BADPROG_MAIN_WINDOW_H

// badprog.com
#include <QMainWindow>

#include "ui_mainWindow.h"

class BadprogOpenGLWidget;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow() override;

protected slots:
    void slotStopStart();
    void slotRadioButtonSwitchColorTexture();
    void slotResetCamera();
    void slotResetObject();
    void slotResetBoth();
    void slotActivateOpenGLScreen();
    void slotSliderLightingColorValueChanged();
    void slotSliderLightingPositionValueChanged();

private:
    Ui::MainWindow ui;
    BadprogOpenGLWidget* _bpOpenGLWidget;
    bool _switcher;
};
#endif // BADPROG_MAIN_WINDOW_H

 

mainWindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>974</width>
    <height>690</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout_4" columnstretch="5,0">
    <item row="0" column="0">
     <widget class="QGroupBox" name="groupBox_openGLScreen">
      <property name="title">
       <string>OpenGLWidget</string>
      </property>
      <layout class="QGridLayout" name="gridLayout_3">
       <item row="0" column="0">
        <layout class="QVBoxLayout" name="verticalLayout"/>
       </item>
      </layout>
     </widget>
    </item>
    <item row="0" column="1">
     <widget class="QGroupBox" name="groupBox_classicalWidgets">
      <property name="maximumSize">
       <size>
        <width>16777215</width>
        <height>16777215</height>
       </size>
      </property>
      <property name="title">
       <string>Classical UI widgets</string>
      </property>
      <layout class="QGridLayout" name="gridLayout_2">
       <item row="0" column="0">
        <layout class="QHBoxLayout" name="horizontalLayout">
         <property name="leftMargin">
          <number>5</number>
         </property>
         <property name="topMargin">
          <number>5</number>
         </property>
         <property name="rightMargin">
          <number>5</number>
         </property>
         <property name="bottomMargin">
          <number>5</number>
         </property>
         <item>
          <widget class="QLabel" name="label_objectRotation">
           <property name="text">
            <string>Object rotation</string>
           </property>
          </widget>
         </item>
         <item>
          <widget class="QCheckBox" name="checkBox">
           <property name="text">
            <string>CheckBox</string>
           </property>
          </widget>
         </item>
        </layout>
       </item>
       <item row="3" column="0">
        <widget class="QPushButton" name="pushButton_resetObject">
         <property name="text">
          <string>PushButton</string>
         </property>
        </widget>
       </item>
       <item row="7" column="0">
        <widget class="QGroupBox" name="groupBox">
         <property name="title">
          <string>Lighting color</string>
         </property>
         <layout class="QGridLayout" name="gridLayout">
          <item row="0" column="0">
           <layout class="QHBoxLayout" name="horizontalLayout_2">
            <property name="leftMargin">
             <number>5</number>
            </property>
            <property name="topMargin">
             <number>5</number>
            </property>
            <property name="rightMargin">
             <number>5</number>
            </property>
            <property name="bottomMargin">
             <number>5</number>
            </property>
            <item>
             <layout class="QVBoxLayout" name="verticalLayout_3">
              <property name="leftMargin">
               <number>5</number>
              </property>
              <property name="topMargin">
               <number>5</number>
              </property>
              <property name="rightMargin">
               <number>5</number>
              </property>
              <property name="bottomMargin">
               <number>5</number>
              </property>
              <item>
               <widget class="QLabel" name="label">
                <property name="text">
                 <string>Red</string>
                </property>
               </widget>
              </item>
              <item>
               <widget class="QSlider" name="verticalSlider_red">
                <property name="maximum">
                 <number>100</number>
                </property>
                <property name="singleStep">
                 <number>0</number>
                </property>
                <property name="orientation">
                 <enum>Qt::Vertical</enum>
                </property>
               </widget>
              </item>
              <item>
               <widget class="QLabel" name="label_sliderValueRed">
                <property name="text">
                 <string>TextLabel</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
            <item>
             <layout class="QVBoxLayout" name="verticalLayout_4">
              <property name="leftMargin">
               <number>5</number>
              </property>
              <property name="topMargin">
               <number>5</number>
              </property>
              <property name="rightMargin">
               <number>5</number>
              </property>
              <property name="bottomMargin">
               <number>5</number>
              </property>
              <item>
               <widget class="QLabel" name="label_2">
                <property name="text">
                 <string>Green</string>
                </property>
               </widget>
              </item>
              <item>
               <widget class="QSlider" name="verticalSlider_green">
                <property name="maximum">
                 <number>100</number>
                </property>
                <property name="orientation">
                 <enum>Qt::Vertical</enum>
                </property>
               </widget>
              </item>
              <item>
               <widget class="QLabel" name="label_sliderValueGreen">
                <property name="text">
                 <string>TextLabel</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
            <item>
             <layout class="QVBoxLayout" name="verticalLayout_5">
              <property name="leftMargin">
               <number>5</number>
              </property>
              <property name="topMargin">
               <number>5</number>
              </property>
              <property name="rightMargin">
               <number>5</number>
              </property>
              <property name="bottomMargin">
               <number>5</number>
              </property>
              <item>
               <widget class="QLabel" name="label_3">
                <property name="text">
                 <string>Blue</string>
                </property>
               </widget>
              </item>
              <item>
               <widget class="QSlider" name="verticalSlider_blue">
                <property name="maximum">
                 <number>100</number>
                </property>
                <property name="tracking">
                 <bool>true</bool>
                </property>
                <property name="orientation">
                 <enum>Qt::Vertical</enum>
                </property>
               </widget>
              </item>
              <item>
               <widget class="QLabel" name="label_sliderValueBlue">
                <property name="text">
                 <string>TextLabel</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
           </layout>
          </item>
         </layout>
        </widget>
       </item>
       <item row="2" column="0">
        <widget class="QPushButton" name="pushButton_resetCamera">
         <property name="text">
          <string>PushButton</string>
         </property>
        </widget>
       </item>
       <item row="4" column="0">
        <widget class="QPushButton" name="pushButton_resetBoth">
         <property name="text">
          <string>PushButton</string>
         </property>
        </widget>
       </item>
       <item row="6" column="0">
        <widget class="QGroupBox" name="groupBox_2">
         <property name="title">
          <string>Lighting position</string>
         </property>
         <layout class="QGridLayout" name="gridLayout_6">
          <item row="0" column="0">
           <layout class="QGridLayout" name="gridLayout_5">
            <property name="leftMargin">
             <number>5</number>
            </property>
            <property name="topMargin">
             <number>5</number>
            </property>
            <property name="rightMargin">
             <number>5</number>
            </property>
            <property name="bottomMargin">
             <number>5</number>
            </property>
            <item row="0" column="0">
             <layout class="QGridLayout" name="gridLayout_7">
              <property name="leftMargin">
               <number>5</number>
              </property>
              <property name="topMargin">
               <number>5</number>
              </property>
              <property name="rightMargin">
               <number>5</number>
              </property>
              <property name="bottomMargin">
               <number>5</number>
              </property>
              <item row="0" column="0">
               <widget class="QLabel" name="label_4">
                <property name="text">
                 <string>X</string>
                </property>
               </widget>
              </item>
              <item row="1" column="0">
               <widget class="QSlider" name="verticalSlider_lightingPositionX">
                <property name="minimum">
                 <number>-100</number>
                </property>
                <property name="maximum">
                 <number>100</number>
                </property>
                <property name="orientation">
                 <enum>Qt::Vertical</enum>
                </property>
               </widget>
              </item>
              <item row="2" column="0">
               <widget class="QLabel" name="label_sliderLightingPositionX">
                <property name="text">
                 <string>TextLabel</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
            <item row="0" column="1">
             <layout class="QGridLayout" name="gridLayout_8">
              <property name="leftMargin">
               <number>5</number>
              </property>
              <property name="topMargin">
               <number>5</number>
              </property>
              <property name="rightMargin">
               <number>5</number>
              </property>
              <property name="bottomMargin">
               <number>5</number>
              </property>
              <item row="0" column="0">
               <widget class="QLabel" name="label_5">
                <property name="text">
                 <string>Y</string>
                </property>
               </widget>
              </item>
              <item row="1" column="0">
               <widget class="QSlider" name="verticalSlider_lightingPositionY">
                <property name="minimum">
                 <number>-100</number>
                </property>
                <property name="maximum">
                 <number>100</number>
                </property>
                <property name="orientation">
                 <enum>Qt::Vertical</enum>
                </property>
               </widget>
              </item>
              <item row="2" column="0">
               <widget class="QLabel" name="label_sliderLightingPositionY">
                <property name="text">
                 <string>TextLabel</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
            <item row="0" column="2">
             <layout class="QGridLayout" name="gridLayout_9">
              <property name="leftMargin">
               <number>5</number>
              </property>
              <property name="topMargin">
               <number>5</number>
              </property>
              <property name="rightMargin">
               <number>5</number>
              </property>
              <property name="bottomMargin">
               <number>5</number>
              </property>
              <item row="0" column="0">
               <widget class="QLabel" name="label_6">
                <property name="text">
                 <string>Z</string>
                </property>
               </widget>
              </item>
              <item row="1" column="0">
               <widget class="QSlider" name="verticalSlider_lightingPositionZ">
                <property name="minimum">
                 <number>-100</number>
                </property>
                <property name="maximum">
                 <number>100</number>
                </property>
                <property name="orientation">
                 <enum>Qt::Vertical</enum>
                </property>
               </widget>
              </item>
              <item row="2" column="0">
               <widget class="QLabel" name="label_sliderLightingPositionZ">
                <property name="text">
                 <string>TextLabel</string>
                </property>
               </widget>
              </item>
             </layout>
            </item>
           </layout>
          </item>
         </layout>
        </widget>
       </item>
       <item row="5" column="0">
        <widget class="QGroupBox" name="groupBox_3">
         <property name="title">
          <string>Type of display</string>
         </property>
         <property name="alignment">
          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
         </property>
         <property name="checkable">
          <bool>false</bool>
         </property>
         <layout class="QFormLayout" name="formLayout">
          <item row="0" column="0">
           <layout class="QVBoxLayout" name="verticalLayout_2">
            <item>
             <widget class="QRadioButton" name="radioButton_color">
              <property name="text">
               <string>Color</string>
              </property>
             </widget>
            </item>
            <item>
             <widget class="QRadioButton" name="radioButton_texture">
              <property name="text">
               <string>Texture</string>
              </property>
             </widget>
            </item>
           </layout>
          </item>
         </layout>
        </widget>
       </item>
      </layout>
     </widget>
    </item>
    <item row="1" column="0">
     <widget class="QLabel" name="label_activateScreenWithMouse">
      <property name="text">
       <string>Touch the OpenGLWidget area with your mouse to activate it.</string>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

 

badprogOpenGLWidget.cpp

#include "badprogOpenglWidget.h"

#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>

#include <QTimer>
#include <QDebug>
#include <QKeyEvent>
#include <QMouseEvent>

#include "input.h"
// badprog.com

// ----------------------------------------------------------------------------
BadprogOpenGLWidget::BadprogOpenGLWidget(QWidget *parent)
    : _vbo2_arrayIndex(QOpenGLBuffer::IndexBuffer)
{
    // Default position.
    _positionDefault = QVector3D(0.0f, 0.0f, -15.0f);
    // Object 1 (Colored cube)
    _transformCubeColor.setTranslation(_positionDefault);
    // Object 3 (Lighting cube)
    _lightPositionDefault = QVector3D(_positionDefault);
    _lightPosition = _positionDefault;
    _transformCubeLighting.setTranslation(_lightPosition);
    _transformCubeLighting.scale(0.01f);
    _NUMBER_OF_ELEMENTS     = 4;
    _NUMBER_OF_ELEMENTS2    = 4;
    _stateRotate            = true;
    _NUMBER_OF_VAO          = 10;
    _vectorLightingColorValues = QVector3D(1.0f, 1.0f, 1.0f);

    _counter = 0;
    _firstRound = true;

    //
    setFocusPolicy(Qt::StrongFocus);

}

// ----------------------------------------------------------------------------
BadprogOpenGLWidget::~BadprogOpenGLWidget() {
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initializeGL() {
    //
    initializeOpenGLFunctions();

    //
    glEnable(GL_DEPTH_TEST);
    glClearColor(0.1f, 0.2f, 0.3f, 0.5f);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glLineWidth(2);

    // ========================================================================
    //
    // Program 1 - Cube color
    //
    // ========================================================================
    initProgramColor();

    // Array of vertex
    StructVertex  arrayVertex[] = {
        // Position                       // Color                     // Texture             // Normal
        { QVector3D(-4.5f, -4.5f,  1.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 0
        { QVector3D( 4.5f, -4.5f,  1.5f), QVector3D(0.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 1
        { QVector3D(-4.5f,  4.5f,  1.5f), QVector3D(0.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 2
        { QVector3D( 4.5f,  4.5f,  1.5f), QVector3D(1.0f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 3

        { QVector3D(-4.5f, -4.5f,  -1.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 4
        { QVector3D( 4.5f, -4.5f,  -1.5f), QVector3D(0.0f, 1.0f, 0.0f), QVector2D(1.0f, 0.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 5
        { QVector3D(-4.5f,  4.5f,  -1.5f), QVector3D(0.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 6
        { QVector3D( 4.5f,  4.5f,  -1.5f), QVector3D(1.0f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f), QVector3D(0.0f, 0.0f, 1.0f) }, // 7
    };

    // Array index
    GLushort arrayIndex[] = {
    0, 1, 2, 3,
    3, 1, 7, 5,
    5, 4, 7, 6,
    6, 2, 7, 3,
    3, 2, 6, 0,
    4, 5, 1, 0
    };

    //
    _NUMBER_OF_ELEMENTS = sizeof(arrayIndex)/ sizeof(GLushort);

    // ------------------------------------------
    // Program 1 - VAO 1
    // ------------------------------------------
    _vao1.create();
    _vao1.bind();

    //
    // VAO 1 - VBO 1 - Buffer 1
    //
    _vbo1_arrayVertex.create();
    _vbo1_arrayVertex.bind();
    _vbo1_arrayVertex.setUsagePattern(QOpenGLBuffer::StreamDraw);
    _vbo1_arrayVertex.allocate(arrayVertex, sizeof (StructVertex) * _NUMBER_OF_ELEMENTS);

    // ========================
    // Position
    //
    // Links information with shaders from the VBO just bound
    // So our position is the position from the "arrayVertex".
    // Buffer Position.
    int bpOffset  = 0; // offset position in the array
    int bpTuple   = 3; // number of element in QVector3D for Position
    int bpStride  = sizeof (StructVertex); // number of elements to skip before the next position (position, color and texture --> QVector3D, QVector3D, QVector2D) in the array

    // Position.
    _programColor->enableAttributeArray(_attribute1_position);
    _programColor->setAttributeBuffer(_attribute1_position, GL_FLOAT, bpOffset, bpTuple, bpStride);

    // ========================
    // Color
    //
    // Buffer Color.
    bpOffset += sizeof (StructVertex::elementPosition); // offset color in the array --> 0 + QVector3D
    bpTuple     = 3;                // number of elements in the QVector3D in the array for Color

    // Color.
    _programColor->enableAttributeArray(_attribute1_color);
    _programColor->setAttributeBuffer(_attribute1_color, GL_FLOAT, bpOffset, bpTuple, bpStride);

    // ========================
    // Texture
    //
    // Buffer Texture.
    bpOffset += sizeof (StructVertex::elementColor); // offset texture in the array --> 0 + QVector3D + QVector3D
    bpTuple     = 2;                // number of elements in the QVector2D in the array for Texture

    // ========================
    // Normal
    //
    // Buffer Normal.
    bpOffset += sizeof (StructVertex::elementTexture); // offset normal in the array --> 0 + QVector3D + QVector3D + QVector2D
    bpTuple     = 3;                // number of elements in the QVector3D in the array for Normal

    // Normal.
    _programColor->enableAttributeArray(_attribute1_normal);
    _programColor->setAttributeBuffer(_attribute1_normal, GL_FLOAT, bpOffset, bpTuple, bpStride);
    // Release - VBO 1
    _vbo1_arrayVertex.release();

    //
    // VAO 1 - VBO 2
    //

    //
    _vbo2_arrayIndex.create();
    _vbo2_arrayIndex.bind();
    _vbo2_arrayIndex.allocate(arrayIndex, sizeof (GLushort) * _NUMBER_OF_ELEMENTS);

    // We do nothing special here because this VBO is an IndexBuffer type.
    // We get all the other buffers from the VBO1 already set.
    // So don't need to re assign them a second time.
    // And as an IndexBuffer we just have to pass "index" from
    // an array to get the StructVertex positions in the "real" array (in our case "arrayVertex").

    // Release VBO 2
    _vbo2_arrayIndex.release();

    // Release VAO 1
    _vao1.release();

    // ------------------------------------------
    // Program Lighting - VAO 1
    // ------------------------------------------

    //
    initProgramLighting();

    //
    _vaoLighting.create();
    _vaoLighting.bind();

    //
    // VAO Lighting - VBO 1 already created, so we just bind it to "activate" it.
    //
    _vbo1_arrayVertex.bind();

    // Position.
    bpOffset  = 0; // offset position in the array
    bpTuple   = 3; // number of element in QVector3D
    bpStride  = sizeof (StructVertex); // number of elements to skip before the next position element in the whole line (position, color and texture --> QVector3D, QVector3D, QVector2D) inthe array

    // Don't need to recreate an allocate() method here because we use the same VBO as in VAO 1.
    // So the elements allocated stay the same just by "binding" it here in this new VAO Lighting.

    // Buffer Position.
    _programLighting->enableAttributeArray(_attributeLightingPosition);
    _programLighting->setAttributeBuffer(_attributeLightingPosition, GL_FLOAT, bpOffset, bpTuple, bpStride);

    // Release VBO1.
    _vbo1_arrayVertex.release();

    // Release VAO Lighting.
    _vaoLighting.release();

    // ========================================================================
    // ========================================================================
    // ========================================================================

    StructVertex  arrayVertex2[] = {
        // Position                       // Color                     // Texture               // Normal
        { QVector3D(-0.5f, -0.5f,  0.5f), QVector3D(1.0f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 0
        { QVector3D(0.0f, 0.5f,  -0.5f), QVector3D(1.0f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 1
        { QVector3D(0.5f, 0.5f,  0.5f), QVector3D(0.2f, 0.8f, 1.0f), QVector2D(0.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 2

        { QVector3D(-0.7f, -0.7f,  0.5f), QVector3D(0.5f, 0.0f, 0.0f), QVector2D(0.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 3
        { QVector3D(-0.1f, -0.5f,  0.5f), QVector3D(0.5f, 0.5f, 0.1f), QVector2D(1.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 4
        { QVector3D(0.7f, 0.7f,  -0.5f), QVector3D(1.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 5

        { QVector3D(-0.3f, -0.3f,  0.5f), QVector3D(0.3f, 0.2f, 0.0f), QVector2D(0.0f, 0.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 6
        { QVector3D(-0.8f, -0.8f,  -0.5f), QVector3D(0.4f, 0.6f, 0.1f), QVector2D(1.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 7
        { QVector3D(0.6f, 0.6f,  -0.5f), QVector3D(1.0f, 0.0f, 1.0f), QVector2D(0.0f, 1.0f), QVector3D(0.0f, 0.0f, -1.0f) }, // 8

    };

    //
    GLushort arrayIndex2[] = { 0, 1, 1, 2, 2, 0 };
    GLushort arrayIndex3[] = { 3, 4, 4, 5, 5, 3 };
    GLushort arrayIndex4[] = { 1, 8, 8, 4, 4, 1 };

    //
    _NUMBER_OF_ELEMENTS2 = sizeof(arrayIndex2)/ sizeof(GLushort);

    //
    createVAO(arrayVertex2, arrayIndex2[0], 4);
    createVAO(arrayVertex2, arrayIndex3[0], 5);
    createVAO(arrayVertex2, arrayIndex4[0], 10);

    //
    initTimer();

    //
    _appareanceType = APPAREANCE_TYPE_COLOR;
    _vector3DForTranslation = QVector3D(0.0f, 0.0f, -4.0f); // default camera position

    //
    QObject::connect(this, &QOpenGLWidget::frameSwapped, this, &BadprogOpenGLWidget::slotFrameSwapped);

}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::createVAO(StructVertex  arrayVertexFromLoop[], GLushort &arrayIndex2, int numberToLoop) {

    //
    if (_firstRound) {
        _counter = 0;
        _firstRound = false;
    } else {
        numberToLoop += _counter;
    }

    //
    while (_counter < numberToLoop) {
        // Fills the vector of VAOs with create() and bind() calls.
        _vectorOfVaos.push_back(new QOpenGLVertexArrayObject());
        _vectorOfVaos.at(_counter)->create();
        _vectorOfVaos.at(_counter)->bind();

        //
        // Creates buffer 1: the VBO Position.
        //
        _vectorOfVboForArray.push_back(new QOpenGLBuffer(QOpenGLBuffer::VertexBuffer));
        _vectorOfVboForArray.at(_counter)->create();
        _vectorOfVboForArray.at(_counter)->bind();

        float x;
        float y;

        float newSize = _counter;

        if (_counter > 10) {
            newSize = _counter / 10;
        }

        x = arrayVertexFromLoop->elementPosition.x() + (_counter / 20);
        y = arrayVertexFromLoop->elementPosition.y() + (_counter / 20);

        arrayVertexFromLoop[0].elementPosition.setX(x);
        arrayVertexFromLoop[0].elementPosition.setY(y);
//        arrayVertexFromLoop[1].elementPosition.setX(x);
//        arrayVertexFromLoop[1].elementPosition.setY(y);
//        arrayVertexFromLoop[2].elementPosition.setX(x);
//        arrayVertexFromLoop[2].elementPosition.setY(y);
        arrayVertexFromLoop[3].elementPosition.setX(x);
        arrayVertexFromLoop[3].elementPosition.setY(y);

        _vectorOfVboForArray.at(_counter)->allocate(arrayVertexFromLoop, sizeof(StructVertex) * _NUMBER_OF_ELEMENTS2);

        // VBO 1: Where is the attribute "position" in the array.
        int bpOffset  = 0; // offset position in the array
        int bpTuple   = 3; // number of element in QVector3D
        int bpStride  = sizeof (StructVertex); // number of elements to skip before the next position (position, color and texture --> QVector3D, QVector3D, QVector2D) inthe array

        // program
        _programColor->enableAttributeArray(_attribute1_position);
        _programColor->setAttributeBuffer(_attribute1_position, GL_FLOAT, bpOffset, bpTuple, bpStride);
        // VBO 1: Where is the attribute "color" in the array.
        bpOffset += sizeof (QVector3D); // offset color in the array
        bpTuple     = 3;                // number of elements in the QVector3D in the array

        // program
        _programColor->enableAttributeArray(_attribute1_color);
        _programColor->setAttributeBuffer(_attribute1_color, GL_FLOAT, bpOffset, bpTuple, bpStride);
        // Buffer Texture.
        bpOffset += sizeof (QVector3D); // offset texture in the array
        bpTuple     = 2;                // number of elements in the QVector3D in the array

        // Buffer Normal.
        bpOffset += sizeof (QVector2D); // offset normal in the array
        bpTuple     = 3;                // number of elements in the QVector3D in the array

        // Normal.
        _programColor->enableAttributeArray(_attribute1_normal);
        _programColor->setAttributeBuffer(_attribute1_normal, GL_FLOAT, bpOffset, bpTuple, bpStride);
        //
        // Creates buffer 2: the VBO Index.
        //
        _vectorOfVboForIndices.push_back(new QOpenGLBuffer(QOpenGLBuffer::IndexBuffer));
        _vectorOfVboForIndices.at(_counter)->create();
        _vectorOfVboForIndices.at(_counter)->bind();
        _vectorOfVboForIndices.at(_counter)->allocate(&arrayIndex2, sizeof (GLushort) * _NUMBER_OF_ELEMENTS2);

        ++_counter;
    }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initTimer() {
    //
    _stateTimer = true;

    //
    _increment = 0.01f;

    //
    _timer = new QTimer(this);

    QObject::connect(_timer, &QTimer::timeout, this, &BadprogOpenGLWidget::slotUpdateTimer);
    _timer->start(1);
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::updateLightingPosition(LIGHTING_POSITION lightingPosition, const QString &sliderValue) {
    //
    float valueFromSlider = sliderValue.toFloat();

    //
    switch (lightingPosition) {
    case LIGHTING_POSITION_X:
    {
        valueFromSlider = _lightPositionDefault.x() + valueFromSlider;
        _lightPosition.setX(valueFromSlider);
    }
    break;
    case LIGHTING_POSITION_Y:
    {
        valueFromSlider = _lightPositionDefault.y() + valueFromSlider;
        _lightPosition.setY(valueFromSlider);
    }
    break;
    case LIGHTING_POSITION_Z:
    {
        valueFromSlider = _lightPositionDefault.z() + valueFromSlider;
        _lightPosition.setZ(valueFromSlider);
    }
    break;
    default:
        break;
    }

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::updateLightingColor(LIGHTING_COLOR lightingColor, const QString &sliderValue) {
    //
    float valueFromSlider = sliderValue.toFloat();
//    float speedFactor = 1;

    //
//    valueFromSlider /= speedFactor;

    //
    switch (lightingColor) {
    case LIGHTING_COLOR_RED:
    {
        _vectorLightingColorValues.setX(valueFromSlider);
    }
        break;
    case LIGHTING_COLOR_GREEN:
    {
        _vectorLightingColorValues.setY(valueFromSlider);
    }
        break;
    case LIGHTING_COLOR_BLUE:
    {
        _vectorLightingColorValues.setZ(valueFromSlider);
    }
        break;
    default:
        break;
    }

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::retrieveAppearence(int appareance) {
    //
    if (APPAREANCE_TYPE_COLOR == appareance) {
        initProgramColor();
    }

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initProgramColor() {
    //
    _appareanceType = APPAREANCE_TYPE_COLOR;

    // Program.
    _programColor = new QOpenGLShaderProgram(this);
    _programColor->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex_color");
    _programColor->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment_color");

    // Linking.
    _programColor->link();

    // Attributes.
    _attribute1_position        = _programColor->attributeLocation("layoutPosition");
    _attribute1_color           = _programColor->attributeLocation("layoutColor");
    _attribute1_normal          = _programColor->attributeLocation("layoutNormal");

    // Uniforms.
    _uniformCubeColorModel          = _programColor->uniformLocation("modelCubeColor");
    _uniformCubeColorView           = _programColor->uniformLocation("viewCubeColor");
    _uniformCubeColorProjection     = _programColor->uniformLocation("projectionCubeColor");
    _uniformCubeColorLightColor     = _programColor->uniformLocation("lightColor");
    _uniformCubeColorLightPosition  = _programColor->uniformLocation("lightPosition");
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::initProgramLighting() {
    // Program.
    _programLighting = new QOpenGLShaderProgram(this);
    _programLighting->addShaderFromSourceFile(QOpenGLShader::Vertex, ":vertex_lighting");
    _programLighting->addShaderFromSourceFile(QOpenGLShader::Fragment, ":fragment_lighting");

    // Linking.
    _programLighting->link();

    // Attributes.
    _attributeLightingPosition = _programLighting->attributeLocation("layoutPosition");

    // Uniforms.
    _uniformCubeLightingModel       = _programLighting->uniformLocation("model");
    _uniformCubeLightingView        = _programLighting->uniformLocation("view");
    _uniformCubeLightingProjection  = _programLighting->uniformLocation("projection");
    _uniformCubeLightingColors      = _programLighting->uniformLocation("uniformColors");
}

// ----------------------------------------------------------------------------
bool BadprogOpenGLWidget::changeTimerState() {
    //
    if (_stateTimer) {
        _timer->stop();
    } else {
        _timer->start(1);
    }

    //
    _stateTimer = !_stateTimer;

    //
    return _stateTimer;
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::slotUpdateTimer() {
    QOpenGLWidget::update();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resizeGL(int width, int height) {
    _projection.setToIdentity();
    _projection.perspective(60.0f, width / float(height), 0.1f, 100.0f);
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::keyPressEvent(QKeyEvent *event) {
    //
    if (event->key() == Qt::Key_Escape) { close(); }
    else if (event->key() == Qt::Key_W) { _key = event->key(); }
    else if (event->key() == Qt::Key_S) { _key = event->key(); }
    else if (event->key() == Qt::Key_A) { _key = event->key(); }
    else if (event->key() == Qt::Key_D) { _key = event->key(); }
    else if (event->key() == Qt::Key_E) { _key = event->key(); }
    else if (event->key() == Qt::Key_Q) { _key = event->key(); }
    else { QWidget::keyPressEvent(event); }

    //   From 3b
    if (event->isAutoRepeat()) {
      event->ignore();
    } else {
      Input::registerKeyPress(event->key());
    }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::keyReleaseEvent(QKeyEvent *event)
{
  if (event->isAutoRepeat())
  {
    event->ignore();
  }
  else
  {
    Input::registerKeyRelease(event->key());
  }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::mousePressEvent(QMouseEvent *event)
{
  Input::registerMousePress(event->button());
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::mouseReleaseEvent(QMouseEvent *event)
{
  Input::registerMouseRelease(event->button());
}
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::slotFrameSwapped() {
    //
    bool result = focusWidget() == this;

    //
    Input::update();

    //
    static const float transSpeed = 0.09f;     // keyboard
    static const float rotSpeed   = 0.05f;    // mouse

    // Camera Transformation
    if (Input::buttonPressed(Qt::LeftButton))     {
      // Handle rotations
      _camera.rotate(-rotSpeed * Input::mouseDelta().x(), Camera3D::LocalUp);
      _camera.rotate(-rotSpeed * Input::mouseDelta().y(), _camera.right());
    }

    // Handle translations
    QVector3D translation;
    if (Input::keyPressed(Qt::Key_W)) { translation += _camera.forward(); }
    if (Input::keyPressed(Qt::Key_S)) { translation -= _camera.forward(); }
    if (Input::keyPressed(Qt::Key_A)) { translation -= _camera.right(); }
    if (Input::keyPressed(Qt::Key_D)) { translation += _camera.right(); }
    if (Input::keyPressed(Qt::Key_Q)) { translation -= _camera.up(); }
    if (Input::keyPressed(Qt::Key_E)) { translation += _camera.up(); }

    // Resets the camera with:
    if (Input::keyPressed(Qt::Key_P)) {
      resetCamera();
    }

    //
    _camera.translate(transSpeed * translation);

    //
    if (_stateRotate) {
      _transformCubeColor.rotate(1.0f, QVector3D(0.4f, 0.3f, 0.3f));
    } else {
      _transformCubeColor.rotate(0.0f, QVector3D(0.0f, 0.0f, 0.0f));
    }

    // Schedule a redraw
    QOpenGLWidget::update();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::rotateSwitcher(bool state) {
    //
    _stateRotate = state;

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetCamera() {
    //
    _camera.setRotation(0.0f, QVector3D(0.0f, 0.0f, 0.0f));
    _camera.setTranslation(QVector3D(0.0f, 0.0f, 0.0f));

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetObject() {
    //
    _transformCubeColor.setRotation(0.0f, QVector3D(0.0f, 0.0f, 0.0f));

    //
    Input::reset();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::resetBoth() {
    //
    resetCamera();
    resetObject();
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::managerUniformForCubeColor() {
    //
    _programColor->setUniformValue(_uniformCubeColorProjection, _projection);
    _programColor->setUniformValue(_uniformCubeColorView, _camera.toMatrix());
    _programColor->setUniformValue(_uniformCubeColorModel, _transformCubeColor.toMatrix());

    // Communicate to the shaders of program1 where are the light cube position and its color.
    _programLighting->setUniformValue(_uniformCubeColorLightColor, _vectorLightingColorValues);
    _programLighting->setUniformValue(_uniformCubeColorLightPosition, _lightPosition);
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::managerUniformForCubeLighting() {
    //
    _transformCubeLighting.setTranslation(_lightPosition); // update the position according to the sliders (X, Y and Z) from the GUI.

    //
    _programLighting->setUniformValue(_uniformCubeLightingProjection, _projection);
    _programLighting->setUniformValue(_uniformCubeLightingView, _camera.toMatrix());
    _programLighting->setUniformValue(_uniformCubeLightingModel, _transformCubeLighting.toMatrix());

    //
    _programLighting->setUniformValue(_uniformCubeLightingColors, _vectorLightingColorValues);
}

// ----------------------------------------------------------------------------
//
// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::paintGL() {
    //
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //
    if (APPAREANCE_TYPE_COLOR == _appareanceType) {
        //
        _programColor->bind();

        //
        managerUniformForCubeColor();
        drawCubeColors();

        //
        drawDynamicShape();

        //
        _programLighting->bind();

        //
        managerUniformForCubeLighting();
        drawCubeLighting();

    }

    //
    if (APPAREANCE_TYPE_COLOR == _appareanceType) {
        _programColor->release();
    }

    //
    _increment += 0.01f;
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::drawCubeColors() {
    // VAO 1
    _vao1.bind();

    // VBO 2 - Index
    _vbo2_arrayIndex.bind();
    glDrawElements(GL_TRIANGLE_STRIP, _NUMBER_OF_ELEMENTS, GL_UNSIGNED_SHORT, 0);
    _vbo2_arrayIndex.release();

    //
    _vao1.release();
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::drawDynamicShape() {
    //
    int i = 0;

    //
    for (auto &element : _vectorOfVaos) {
        element->bind();
        _vectorOfVboForIndices.at(i)->bind();
        glDrawElements(GL_LINES, _NUMBER_OF_ELEMENTS2, GL_UNSIGNED_SHORT, 0);
        _vectorOfVboForIndices.at(i)->release();
        ++i;
    }
}

// ----------------------------------------------------------------------------
void BadprogOpenGLWidget::drawCubeLighting() {
    // VAO 1
    _vaoLighting.bind();

    // VBO 2 - Index
    _vbo2_arrayIndex.bind();
    glDrawElements(GL_TRIANGLE_STRIP, _NUMBER_OF_ELEMENTS, GL_UNSIGNED_SHORT, 0);
    _vbo2_arrayIndex.release();

    //
    _vaoLighting.release();
}

 

badprogOpenGLWidget.h

#ifndef BADPROG_OPENGL_WIDGET_H
#define BADPROG_OPENGL_WIDGET_H

// badprog.com
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include "camera3d.h"
#include "transform3d.h"

class QTimer;
class QWidget;
class QOpenGLTexture;
class QKeyEvent;
class QMouseEvent;

//
enum APPAREANCE_TYPE {
    APPAREANCE_TYPE_COLOR,
    APPAREANCE_TYPE_TEXTURE,
    APPAREANCE_TYPE_SIZE
};

//
enum LIGHTING_COLOR {
    LIGHTING_COLOR_RED,
    LIGHTING_COLOR_GREEN,
    LIGHTING_COLOR_BLUE,
    LIGHTING_COLOR_SIZE
};

//
enum LIGHTING_POSITION{
    LIGHTING_POSITION_X,
    LIGHTING_POSITION_Y,
    LIGHTING_POSITION_Z,
    LIGHTING_POSITION_SIZE
};

//
struct StructVertex {
    QVector3D elementPosition;
    QVector3D elementColor;
    QVector2D elementTexture;
    QVector3D elementNormal;
};

class BadprogOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
    BadprogOpenGLWidget();
    BadprogOpenGLWidget(QWidget *parent = nullptr);
    ~BadprogOpenGLWidget() override;

    // stuff
    static const QVector3D LocalForward;
    static const QVector3D LocalUp;
    static const QVector3D LocalRight;

    //
    bool changeTimerState();
    void initArraysAndVAO1();
    void initArraysAndVAO2();
    void initProgramColor();
    void initProgramLighting();
    void initTimer();
    void retrieveAppearence(int appareance);
    void goDirection(int letter);
    void rotateSwitcher(bool state);
    void resetCamera();
    void resetObject();
    void resetBoth();
    void managerUniformForCubeColor();
    void managerUniformForCubeLighting();
    void drawCubeColors();
    void drawDynamicShape();
    void drawCubeLighting();
    void createVAO(StructVertex  arrayVertexFromLoop[], GLushort &arrayIndex2, int numberToLoop);
    void updateLightingColor(LIGHTING_COLOR lightingColor, const QString &sliderValue);
    void updateLightingPosition(LIGHTING_POSITION lightingPosition, const QString &sliderValue);

public slots:
    void slotUpdateTimer();
    void slotFrameSwapped();

protected:
    // QOpenGLWidget interface
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;
    void keyPressEvent(QKeyEvent *event) override; // From QWidget
    void keyReleaseEvent(QKeyEvent *event) override; // From QWidget
    void mousePressEvent(QMouseEvent *event) override; // From QWidget
    void mouseReleaseEvent(QMouseEvent *event) override; // From QWidget

    // methods
private:
    // variables
private:
    // new stuff
    QMatrix4x4 _projection;
    GLuint _uniformPointSize;

    // Cube color.
    GLuint _uniformCubeColorLightColor;
    GLuint _uniformCubeColorLightPosition;
    GLuint _uniformCubeColorProjection;
    GLuint _uniformCubeColorView;
    GLuint _uniformCubeColorModel;

    // Cube lightning.
    GLuint _uniformCubeLightingColors;
    GLuint _uniformCubeLightingProjection;
    GLuint _uniformCubeLightingView;
    GLuint _uniformCubeLightingModel;

    //
    GLuint _goForward;
    GLuint _goX;
    GLuint _goY;
    int _NUMBER_OF_ELEMENTS;
    int _NUMBER_OF_ELEMENTS2;
    QQuaternion _quaternion;
    QVector3D _vector3DForTranslation;
    GLfloat _increment;

    std::vector<QOpenGLVertexArrayObject *> _vectorOfVaos;
    std::vector<QOpenGLBuffer *> _vectorOfVboForArray;
    std::vector<QOpenGLBuffer *> _vectorOfVboForIndices;

    // ========================================================================
    // Color
    // ========================================================================

    // 1st object
    QOpenGLShaderProgram* _programColor;
    GLuint _attribute1_position;
    GLuint _attribute1_color;
    GLuint _attribute1_normal;
    GLuint _uniformForShader1_matrix;

    // 2nd object
    GLuint _attribute3_position;
    GLuint _attribute3_color;
    GLuint _uniformForShader3_matrix;

    // ========================================================================
    // Lighting
    // ========================================================================
    QOpenGLShaderProgram* _programLighting;
    GLuint _attributeLightingPosition;
    GLuint _attributeLightingColor;
    GLuint _uniformForShaderMatrixLighting;

    // VAO, VBO
    QOpenGLVertexArrayObject _vao1;
    QOpenGLVertexArrayObject _vaoLighting;
    QOpenGLBuffer _vbo1_arrayVertex;
    QOpenGLBuffer _vbo2_arrayIndex;

    QOpenGLBuffer::Type _vbo2_index;

    //
    QTimer* _timer;
    bool _stateTimer;
    APPAREANCE_TYPE _appareanceType;

    int _key;

    Camera3D _camera;
    Transform3D _transformCubeColor;
    Transform3D _transformCubeLighting;

    bool _stateRotate;
    bool _stateOriginal;
    QMatrix4x4 _matrixOriginalCamera;
    QMatrix4x4 _matrixOriginalTransform;
    QMatrix4x4 _matrixOriginalTransform2;
    bool _stateResetCamera;
    bool _stateResetTransform;
    float _pointSize;
    float _NUMBER_OF_VAO;
    float _counter;
    bool _firstRound;

    QVector3D _vectorLightingColorValues;
    QVector3D _lightPosition;
    QVector3D _positionDefault;
    QVector3D _lightPositionDefault;
};

#endif // !BADPROG_OPENGL_WIDGET_H

 

badprog.qrc

<RCC>

    <qresource prefix="/">

        <file alias="fragment_color">shader/shader_fragment_color.glsl</file>

        <file alias="vertex_color">shader/shader_vertex_color.glsl</file>

        <file alias="fragment_lighting">shader/shader_fragment_lighting.glsl</file>

        <file alias="vertex_lighting">shader/shader_vertex_lighting.glsl</file>

    </qresource>

</RCC>
 

shader/shader_vertex_color.glsl

 

#version 330 core
// badprog.com

// layout
layout (location = 0) in vec3 layoutPosition;
layout (location = 1) in vec3 layoutColor;
//layout (location = 2) in vec3 layoutTexture;
layout (location = 3) in vec3 layoutNormal;

// out
out vec3 shaderColor;
out vec3 shaderNormal;
out vec3 fragmentPosition;

// uniform
uniform mat4 modelCubeColor;
uniform mat4 viewCubeColor;
uniform mat4 projectionCubeColor;
uniform float pointSize;

// ----------------------------------------------------------------------------
void main() {
    //
    fragmentPosition = vec3(modelCubeColor * vec4(layoutPosition, 1.0));
    shaderNormal = layoutNormal;
    gl_Position =  projectionCubeColor * viewCubeColor * vec4(fragmentPosition, 1.0f);

    gl_PointSize = pointSize;
    shaderColor = layoutColor;
}

 

shader/shader_fragment_color.glsl

#version 330 core
// badprog.com

// out
out vec4 FragColor;

// in
in vec3 shaderColor;
in vec3 shaderNormal;
in vec3 fragmentPosition;

//
uniform vec3 lightPosition;
uniform vec3 lightColor;
uniform vec3 cubeColor;

// ----------------------------------------------------------------------------
void main() {
    // ambient
    float ambientStrength = 0.3;
    vec3 ambient = ambientStrength * lightColor;

    // diffuse
    vec3 vectorNormal       = normalize(shaderNormal);
    vec3 vectorDifference   = normalize(lightPosition - fragmentPosition);
    float dotProduct        = dot(vectorNormal, vectorDifference);
    float maxValue          = max(dotProduct, 0.0);
    vec3 diffuse            = maxValue * lightColor;
    vec3 result             = (ambient + diffuse) * shaderColor;

    //
    FragColor = vec4(result, 1.0);
}

 

shader/shader_vertex_ligthing.glsl

#version 330 core
// badprog.com

// layout
layout (location = 0) in vec3 layoutPosition;
//layout (location = 1) in vec3 layoutColor;
//layout (location = 2) in vec3 layoutTexture;

// out
out vec3 shaderColor;

// uniform
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

uniform vec3 uniformColors;

// ----------------------------------------------------------------------------
void main() {
    //
    gl_Position =  projection * view * model * vec4(layoutPosition, 1.0f);
    shaderColor = uniformColors;
}

 

shader/shader_fragment_lighting.glsl

#version 330 core
// badprog.com

// out
out vec4 FragColor;

// in
in vec3 shaderColor;

// ----------------------------------------------------------------------------
void main() {
    //
    FragColor = vec4(shaderColor, 1.0f);
}

 

Conclusion

The shaders are a great help to display a lighting object.

We can use tons of mathematic concepts in order to achieve all our truly desire.

Good job, once again you did it. heart

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.