Уроки, алгоритмы, программы, примеры

Вход на сайт

Материалы по разделам

Построения
на плоскости (2D)
Графика
в пространстве (3D)
Вычислительная
геометрия
Физическое
моделирование
Фрактальная
графика

Новые комментарии

Не получается, емаё
огромное спасибо за подробное объяснение про 3д графику на питоне, в интернете очень мало подобной информации
dobryj den, popytalas otkryt prikreplionnyj fail ctoby posmotret kak rabotaet, no mne ego ne pokazyvaet vydajet osibku. Pochemu?
Очень интересно! ии сайт крутой жалко что умирает(
У Вас число превысит максимальное число int. Можно использовать в Вашем случае uint, но лучше все переписать на double.

Счетчики и рейтинг

Рейтинг@Mail.ru Яндекс.Метрика
Скриншот к примеру
Среда программирования: 
Qt
Статья по теме: 

Создание сцены при помощи функций OpenGL и языка GLSL для построение трёхмерных объектов в среде Qt Creater.

Код программы: 

Содержание mainglwidget.h
#ifndef MAINGLWIDGET_H
#define MAINGLWIDGET_H
 
#include <QGLWidget>
#include <QMouseEvent>
#include <QMatrix4x4>
#include <QGLShaderProgram>
#include <QCoreApplication>
#include <math.h>
#include <iostream>
#include <QVector>
#include <QPair>
 
#define CUBE_GRAN 1.0f
//четное
#define EARTH_SIZE 10
#define COUNT_TREE 12
using namespace std;
class MainGLWidget : public QGLWidget
{
 
public:
    MainGLWidget(QWidget *);
    void run();
private:
    int sunAngle = 270;
    int angleSpeed = 1;
    QVector<QPair<int, QPointF>> trees; //высота дерева и его координата
    QVector<int> cloudE = {1,0,1,1,0,0,1,1,0,1}; //какие сферы будут рисоваться
    QPointF cloudP = {5,5}; //позиция облака
    QPointF cloudS = {1,0.5}; //шаг облака
    QVector<QVector3D> dropVector; //вектор капель
    // Матрица поворота
    QMatrix4x4 rotateMatrix;
 
    // Позиция указателя мыши
    QPoint mousePosition;
    double k = 0.03; //на сколько уменьшать/увеличивать изображение
 
    // Процедура для изменения матрицы проектирования
    void resetProjection();
 
    // Процедура для изменения видовой матрицы
    void resetModelView();
 
    // Инициализация шейдеров
    void initShader(void);
    void initTextures();
 
    void initializeGL();
    void resizeGL(int nWidth, int nHeight);
    void paintGL();
    void mouseMoveEvent(QMouseEvent* m_event);
    void mousePressEvent(QMouseEvent* m_event);
    void keyPressEvent(QKeyEvent* key_event);
    void wheelEvent(QWheelEvent *event);
    void closeEvent(QCloseEvent *event);
 
    void glEarth();
    void glHome();
    void glSun();
    void glTrees();
    void glCloud();
    void glDrop();
 
    void stepCloud();
    void upLight();
 
    // Матрица видового преобразования
    QMatrix4x4 modelViewMatrix;
 
    // Матрица проектирования
    QMatrix4x4 projectMatrix;
 
    QGLShaderProgram shader_program;
 
    //айдишники того что передается в шейдеры
    int vertexLocation;
    int matrixLocation;
    int colorLocation;
    int imposterCheckLocation;
    int defAlways;
 
    int normLoc;
    int normMatrLoc;
    int lightPos;
    int defCLoc;
    int texLoc;
    GLuint texs[3];
};
 
#endif // MAINGLWIDGET_H
 
Содержание mainglwidget.cpp
#include "mainglwidget.h"
#include <GL/glu.h>
#include <QDebug>
#include <QMatrix4x4>
#include <iostream>
 
using namespace std
;
MainGLWidget::MainGLWidget(QWidget *parent)
    : QGLWidget(QGLFormat(), parent)
{
    setWindowTitle("Home in forest");
    setWindowState(Qt::WindowFullScreen); //развернуть на весь экран
    for (int i = -9; i<10; i+=3) //сгенерировать деревья
    {
        for (int j=-9;j<10;j+=3)
        {
            if((i<-8 or i>-5) and(j<-8 or j>-5)) //не рисовать деревья там, где стоит домик
                if(rand()%3)
                    trees.push_back({rand()%3+4, {i+(rand()%4)*0.25, j+(rand()%4)*0.25}}); //записать случайную координату в вектор
        }
    }
}
 
void MainGLWidget::initializeGL()
{
    // Включение сортировки по глубине, чтобы то, что ближе, отрисовывалось ближе, а то, что дальше, дальше
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D); //включить текстуры
 
    // Инициализируем видовую матрицу
    resetModelView();
    //Инициализируем шейдеры
    initShader();
    initTextures();
}
 
void MainGLWidget::initShader(void)
{
    // Текст вершинного шейдера
    shader_program.addShaderFromSourceFile(QGLShader::Vertex, ":/vertexShader.vsh");
 
    // Текст фрагментного шейдера
    shader_program.addShaderFromSourceFile(QGLShader::Fragment, ":/fragmentShader.fsh");
 
    if (shader_program.link() == false)
        qDebug() << shader_program.log();
 
    // Достаем идентификатор массива вершин
    vertexLocation = shader_program.attributeLocation("vertex");
 
    texLoc = shader_program.attributeLocation("texture");
 
    // Достаем идентификатор матрицы
    matrixLocation = shader_program.uniformLocation("matrix");
 
    // Идентификатор цвета
    colorLocation =  shader_program.uniformLocation("color");
 
    defAlways =  shader_program.uniformLocation("defAlways");
 
    normLoc = shader_program.attributeLocation("normal");
    normMatrLoc =  shader_program.uniformLocation("normalMatrix");
    lightPos =  shader_program.uniformLocation("lightPos");
    defCLoc =  shader_program.uniformLocation("defC");
}
 
void MainGLWidget::initTextures()
{
    QImage texture;
    glGenTextures(5, texs); //сгенерировать внутри шейдера место под текстуры
 
    texture.load(":/UP.png"); //загрузить изображение в память
    texture = convertToGLFormat(texture); //преобразовать изображение к gl-формату
    glBindTexture(GL_TEXTURE_2D, texs[0]); //выбираем текстуру [0]
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    //загрузить текстуру в слот в шейдере
    glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei(texture.width()), GLsizei(texture.height()), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
 
    texture.load(":/Wall.png"); //загрузить изображение в память
    texture = convertToGLFormat(texture); //преобразовать изображение к gl-формату
    glBindTexture(GL_TEXTURE_2D, texs[1]); //выбираем текстуру [1]
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    //загрузить текстуру в слот в шейдере
    glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei(texture.width()), GLsizei(texture.height()), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
 
    texture.load(":/Earth.png"); //загрузить изображение в память
    texture = convertToGLFormat(texture); //преобразовать изображение к gl-формату
    glBindTexture(GL_TEXTURE_2D, texs[2]); //выбираем текстуру [2]
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    //загрузить текстуру в слот в шейдере
    glTexImage2D(GL_TEXTURE_2D, 0, 3, GLsizei(texture.width()), GLsizei(texture.height()), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.bits());
}
 
void MainGLWidget::resizeGL(int width, int height) //изменение зоны рисования
{
    glViewport(0, 0, width, height);
    //перегенерируем матрицу проектирования
    resetProjection();
}
 
// Внутри данной подпрограммы происходит рисование объектов
void MainGLWidget::paintGL()
{
    // Очистка буфера глубины и буфера цвета
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    resetModelView(); //новая видовая матрица
    glEarth(); //рисует землю
    glHome(); //рисует дом
    glSun(); //рисует солнце и луну
    glTrees(); //рисует деревья
    glCloud(); //рисует облако
    glDrop(); //рисует дождь
}
 
void MainGLWidget::resetProjection()
{
    projectMatrix.setToIdentity(); //заполнить матрицу единицами
    // Умножение на матрицу перспективного проектирования
    projectMatrix.perspective(30.0, (float)width() / height(), 0.1, 20); //проецирует на плоскость так, чтобы картинка была объемной
}
 
// Процедура для изменения видовой матрицы
void MainGLWidget::resetModelView()
{
    modelViewMatrix.setToIdentity(); //инициализировать единичной матрицей
 
    //двигаем сцену, чтобы ее было видно
    modelViewMatrix.translate(0, 0, -3);
    //скалируем координаты, чтобы было удобнее отрисовывать и передвигать объекты
    modelViewMatrix.scale(k, k, k); //увеличивать/уменьшать сцену
    modelViewMatrix.rotate(20, 1, 0, 0); //поворачивает сцену
    modelViewMatrix *= rotateMatrix.transposed(); //реализация вращения
}
 
// Обработчик события перемещения указателя мыши (событие происходит при зажатой кнопке мыши)
void MainGLWidget::mouseMoveEvent(QMouseEvent* m_event)
{
    // Вычислим, на сколько переместился курсор мыши между двумя событиями mouseMoveEvent
    QPoint dp = m_event->pos() - mousePosition;
    // Изменим матрицу поворота в соответствии с тем, как пользователь переместил курсор мыши
    rotateMatrix.rotate(-dp.x()/5., 0, 1, 0);
    rotateMatrix.rotate(-dp.y()/5., 1, 0, 0);
    // Сохраним текущую позицию мыши
    mousePosition = m_event->pos();
    // Обновим матрицу аффинных преобразований
    resetModelView();
    updateGL(); // Перерисовать окно
}
 
void MainGLWidget::mousePressEvent(QMouseEvent* m_event)
{
    //Сохраняем позицию после нажатия что бы картинка не дергалась
    mousePosition = m_event->pos();
}
 
void MainGLWidget::keyPressEvent(QKeyEvent *event)
{
    // Закрыть окно при нажатии клавиши Escape
    if (event->key() == Qt::Key_Escape)
        exit(0);
    if ((event->key() == 1067) or (event->key() == Qt::Key_S))
        if (angleSpeed)
            angleSpeed = 0;
        else
            angleSpeed = 1;
}
 
void MainGLWidget::wheelEvent(QWheelEvent *event)
{
    k += event->delta()/10000.;
    if (k>0.99)
        k=1.0;
    if (k<0.001)
        k=0.001;
    update();
}
 
void MainGLWidget::closeEvent(QCloseEvent *event)
{
    exit(0);
}
 
void MainGLWidget::glEarth()
{    
    static const float vertices[6*4][3] =
                    {{ CUBE_GRAN,  CUBE_GRAN,  CUBE_GRAN}, { CUBE_GRAN,  CUBE_GRAN, -CUBE_GRAN}, { -CUBE_GRAN, CUBE_GRAN,  -CUBE_GRAN},{ -CUBE_GRAN,  CUBE_GRAN,  CUBE_GRAN},
                     { CUBE_GRAN,  -CUBE_GRAN,  CUBE_GRAN}, { CUBE_GRAN,  -CUBE_GRAN, -CUBE_GRAN}, { -CUBE_GRAN, -CUBE_GRAN,  -CUBE_GRAN}, { -CUBE_GRAN,  -CUBE_GRAN,  CUBE_GRAN},
                     { CUBE_GRAN,  CUBE_GRAN,  CUBE_GRAN}, { CUBE_GRAN,  CUBE_GRAN, -CUBE_GRAN}, { CUBE_GRAN,  -CUBE_GRAN, -CUBE_GRAN}, { CUBE_GRAN,  -CUBE_GRAN,  CUBE_GRAN},
                     { CUBE_GRAN,  CUBE_GRAN, -CUBE_GRAN}, { -CUBE_GRAN, CUBE_GRAN,  -CUBE_GRAN}, { -CUBE_GRAN, -CUBE_GRAN,  -CUBE_GRAN}, { CUBE_GRAN,  -CUBE_GRAN, -CUBE_GRAN},
                     { -CUBE_GRAN, CUBE_GRAN,  -CUBE_GRAN}, { -CUBE_GRAN,  CUBE_GRAN,  CUBE_GRAN}, { -CUBE_GRAN,  -CUBE_GRAN,  CUBE_GRAN},  { -CUBE_GRAN, -CUBE_GRAN,  -CUBE_GRAN},
                    { -CUBE_GRAN,  CUBE_GRAN,  CUBE_GRAN}, { CUBE_GRAN,  CUBE_GRAN,  CUBE_GRAN}, { CUBE_GRAN,  -CUBE_GRAN,  CUBE_GRAN}, { -CUBE_GRAN,  -CUBE_GRAN,  CUBE_GRAN},
                     }; //1 кубик из платформы, на которой все держится
 
    static const float normals[6*4][3] =
    {{0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 1.0, 0.0},
     {0.0, -1.0, 0.0},{0.0, -1.0, 0.0},{0.0, -1.0, 0.0},{0.0, -1.0, 0.0},
     {1.0, 0.0, 0.0},{1.0, 0.0, 0.0},{1.0, 0.0, 0.0},{1.0, 0.0, 0.0},
     {0.0, 0.0, -1.0},{0.0, 0.0, -1.0},{0.0, 0.0, -1.0},{0.0, 0.0, -1.0},
     {-1.0, 0.0, 0.0},{-1.0, 0.0, 0.0},{-1.0, 0.0, 0.0},{-1.0, 0.0, 0.0},
     {0.0, 0.0, 1.0},{0.0, 0.0, 1.0},{0.0, 0.0, 1.0},{0.0, 0.0, 1.0},
    }; //нормали для граней кубика, чтобы определить освещение
    float tex[6*4][2]{
        {0.25, 0.3333333}, {0.5, 0.3333333}, {0.5, 0.6666666}, {0.25, 0.6666666},
        {0.75, 0.3333333}, {1.0, 0.3333333}, {1.0, 0.6666666}, {0.75, 0.6666666},
        {0.5, 0.6666666},  {0.5, 0.3333333}, {0.75, 0.3333333}, {0.75, 0.6666666},
        {0.25, 0.3333333}, {0.25, 0.6666666}, {0.0, 0.6666666},  {0.0, 0.3333333},
        {0.25, 0.6666666}, {0.5, 0.6666666}, {0.5, 1.0}, {0.25, 1.0},
        {0.5, 0.3333333}, {0.25, 0.3333333}, {0.25, 0.0}, {0.5, 0.0},
    }; //текстурные координаты
    shader_program.bind(); //включение шейдера
    upLight(); //задаем свет
    glBindTexture(GL_TEXTURE_2D, texs[2]); //выбираем текстуру земли
    shader_program.setAttributeArray(vertexLocation, (float*)vertices, 3); //передача массива вершин в шейдер
    shader_program.setAttributeArray(normLoc, (float*)normals, 3); //передача массива нормалей в шейдер
    shader_program.setAttributeArray(texLoc, (float*)tex, 2); //передача текстурных координат в шейдер
 
    shader_program.setUniformValue(defAlways, false); //включить проверку на выделение граней кубика
 
    shader_program.enableAttributeArray(vertexLocation); //включить масив вершин, переданный в шейдер
    shader_program.enableAttributeArray(normLoc); //включить масив нормалей, переданный в шейдер
    shader_program.enableAttributeArray(texLoc); //включить текстурные координаты, переданный в шейдер
 
    shader_program.setUniformValue(colorLocation, QColor(162, 101, 62)); //передать свет земли в шейдер
   for (int k=0; k<EARTH_SIZE-1; ++k) //идем по уровням земли
   {
    for (int i=0; i<EARTH_SIZE-k; ++i) //ближе/дальше
    {
        for (int j=0; j<EARTH_SIZE-k; ++j) //вправо/влево
        {
            QMatrix4x4 m_xyz;
            m_xyz.translate((EARTH_SIZE-k-1) - j*2, -k*2, (EARTH_SIZE-k-1) - i*2); //поставить кубик на нужное место
            //m_xyz.rotate(90*(k+i+j%5), 0, 1, 0);
            //передача значений переменных в шейдер
            //matrixLocation - айдишник переменной внутри шейдера,
            //projectMatrix - матрица проектирования,
            //modelViewMatrix - матрица для положения сцены,
            //m_xyz - матрица для положения конкретного кубика
            shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*m_xyz);
            shader_program.setUniformValue(normMatrLoc, m_xyz); //передать матрицу нормалей в шейдер
            glDrawArrays(GL_QUADS,0 ,6*4); //отрисовка всего переданного
        }
    }
   }
 
    shader_program.disableAttributeArray(vertexLocation); //выключение массива вершин
 
    shader_program.release(); //отрисовка всего переданного
}
 
void MainGLWidget::glHome()
{
    float verUp[2*4][3]={
        {0.0,  4.0,  2.0},{0.0,  4.0,  -2.0},{-2.0,  2.0,  -2.0},{-2.0,  2.0,  2.0},
        {0.0,  4.0,  2.0},{0.0,  4.0,  -2.0},{2.0,  2.0,  -2.0},{2.0,  2.0,  2.0},
    }; //задаем вершины для крыши дома
    float normUp[2*4][3]={
        {-0.5,  0.5,  0.0},{-0.5,  0.5,  0.0},{-0.5,  0.5,  0.0},{-0.5,  0.5,  0.0},
        {0.5,  0.5,  0.0},{0.5,  0.5,  0.0},{0.5,  0.5,  0.0},{0.5,  0.5,  0.0},
    }; //задаем нормали для крыши дома
    float texUp[][2]{
         {0.0, 0.0},{4.0, 0.0},{4.0, 1.0},{0.0, 1.0},
         {0.0, 0.0},{4.0, 0.0},{4.0, 1.0},{0.0, 1.0},
 
    }; //задаем текстуры для крыши дома
 
    //приведение координат к интервалу [-1, 1]
    for (int i = 0; i < 2*4; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            verUp[i][j] = verUp[i][j]/4;
        }
    }
 
    float verRoof[2*4][3]{
        {-2.0,  2.0,  2.0},{0.0,  4.0,  2.0},{2.0,  2.0,  2.0},
        {-2.0,  2.0,  -2.0},{0.0,  4.0,  -2.0},{2.0,  2.0,  -2.0},
    }; //задаем вершины для треугольников под крышей дома
    float normRoof[2*4][3]{
        {0.0,  0.0,  1.0},{0.0,  0.0,  1.0},{0.0,  0.0,  1.0},
        {0.0,  0.0,  -1.0},{0.0,  0.0,  -1.0},{0.0,  0.0,  -1.0},
    }; //задаем нормали для треугольников под крышей дома
    float texRoof[][2]{
         {0.3, 0.0},{0.5, 1.0},{0.7, 0.0},
         {0.3, 0.0},{0.5, 1.0},{0.7, 0.0},
 
    }; //задаем текстуры для треугольников под крышей дома
 
    //приведение координат к интервалу [-1, 1]
    for (int i = 0; i < 2*4; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            verRoof[i][j] = verRoof[i][j]/4;
        }
    }
 
    float verWall[][3] = {
        {-2.0,  0.0,  2.0}, {-0.3,  0.0,  2.0}, {-0.3,  2.0,  2.0}, {-2.0,  2.0,  2.0},
        {-2.0,  0.0,  2.0}, {-2.0,  0.0,  -2.0}, {-2.0,  2.0,  -2.0}, {-2.0,  2.0,  2.0},
        {-2.0,  2.0,  -2.0}, {2.0,  2.0,  -2.0}, {2.0,  0.0,  -2.0}, {-2.0,  0.0,  -2.0},
        {2.0,  0.0,  -2.0}, {2.0,  2.0,  -2.0}, {2.0,  2.0,  2.0}, {2.0,  0.0,  2.0},
        {0.3,  0.0,  2.0},{2.0,  0.0,  2.0},{2.0,  2.0,  2.0},{0.3,  2.0,  2.0},
 
    }; //задаем вершины для стен дома
 
    float normWall[][3] = {
        {0.0,  0.0,  1.0},{0.0,  0.0,  1.0},{0.0,  0.0,  1.0},{0.0,  0.0,  1.0},
        {-1.0,  0.0,  0.0},{-1.0,  0.0,  0.0},{-1.0,  0.0,  0.0},{-1.0,  0.0,  0.0},
        {0.0,  0.0,  -1.0},{0.0,  0.0,  -1.0},{0.0,  0.0,  -1.0},{0.0,  0.0,  -1.0},
        {1.0,  0.0,  0.0},{1.0,  0.0,  0.0},{1.0,  0.0,  0.0},{1.0,  0.0,  0.0},
        {0.0,  0.0,  1.0},{0.0,  0.0,  1.0},{0.0,  0.0,  1.0},{0.0,  0.0,  1.0},
    }; //задаем нормали для стен дома
 
    float texWall[][2]{
        {0.0, 0.0},{1.0, 0.0},{1.0, 1.0},{0.0, 1.0},
        {0.0, 0.0},{1.0, 0.0},{1.0, 1.0},{0.0, 1.0},
        {0.0, 0.0},{1.0, 0.0},{1.0, 1.0},{0.0, 1.0},
        {0.0, 1.0},{0.0, 0.0},{1.0, 0.0},{1.0, 1.0},
        {0.0, 0.0},{1.0, 0.0},{1.0, 1.0},{0.0, 1.0},
    }; //задаем текстуры для стен дома
 
    //приведение координат к интервалу [-1, 1]
    for (int i = 0; i < 5*4; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            verWall[i][j] = verWall[i][j]/2;
        }
    }
 
    shader_program.bind(); //активация шейдера
 
    upLight(); //подключение света
 
    shader_program.setAttributeArray(vertexLocation, (float*)verWall, 3); //передача массива вершин в шейдер
    shader_program.setAttributeArray(normLoc, (float*)normWall, 3); //передача массива нормалей в шейдер
    shader_program.setAttributeArray(texLoc, (float*)texWall, 2); //передача текстурных координат в шейдер
 
    shader_program.setUniformValue(defAlways, true); //включаем дефолтный цвет
 
    shader_program.enableAttributeArray(vertexLocation); //включить массив вершин, переданный в шейдер
    shader_program.enableAttributeArray(normLoc); //включить массив нормалей, переданный в шейдер
    shader_program.enableAttributeArray(texLoc); //включить текстурные координаты, переданный в шейдер
 
    shader_program.setUniformValue(colorLocation, QColor(162, 101, 62)); //включаем цвет
 
    QMatrix4x4 r_t;
    r_t.setToIdentity(); //идентифицируем единичной матрицей
    r_t.translate(-EARTH_SIZE/2.,0,-EARTH_SIZE/2.); //перемещает дом в левый дальний угол
    QMatrix4x4 sc;
    sc.scale(2, 2, 2); //увеличивает координаты после их уменьшения до интервала [-1, 1]
 
    //передача значений переменных в шейдер
    //matrixLocation - айдишник переменной внутри шейдера,
    //projectMatrix - матрица проектирования,
    //modelViewMatrix - матрица для положения сцены
    //r_t - положение дома
    //sc - матрица увеличенных координаты после их уменьшения до интервала [-1, 1]
    shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*r_t*sc);
    shader_program.setUniformValue(normMatrLoc, r_t); //передать матрицу нормалей и матрицу для их изменения при повороте
    glBindTexture(GL_TEXTURE_2D, texs[1]); //включаем текстуру для стен дома
    glDrawArrays(GL_QUADS,0 ,5*4); //отрисовка
 
    shader_program.setAttributeArray(vertexLocation, (float*)verRoof, 3); //передача массива вершин в шейдер
    shader_program.setAttributeArray(normLoc, (float*)normRoof, 3); //передача массива нормалей в шейдер
    shader_program.setAttributeArray(texLoc, (float*)texRoof, 2); //передача текстурных координат в шейдер
 
    shader_program.enableAttributeArray(vertexLocation); //включить массив вершин, переданный в шейдер
    shader_program.enableAttributeArray(normLoc); //включить массив нормалей, переданный в шейдер
    shader_program.enableAttributeArray(texLoc); //включить текстурные координаты, переданный в шейдер
 
    sc.scale(2, 2, 2); //увеличивает координаты после их уменьшения до интервала [-1, 1]
 
    //передача значений переменных в шейдер
    //matrixLocation - айдишник переменной внутри шейдера,
    //projectMatrix - матрица проектирования,
    //modelViewMatrix - матрица для положения сцены
    //r_t - положение дома
    //sc - матрица увеличенных координаты после их уменьшения до интервала [-1, 1]
    shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*r_t*sc);
    glDrawArrays(GL_TRIANGLES,0 ,2*3); //отрисовка
 
    glBindTexture(GL_TEXTURE_2D, texs[0]); //включаем текстуру для треугольников под крышей дома
 
    shader_program.setAttributeArray(vertexLocation, (float*)verUp, 3); //передача массива вершин в шейдер
    shader_program.setAttributeArray(normLoc, (float*)normUp, 3); //передача массива нормалей в шейдер
    shader_program.setAttributeArray(texLoc, (float*)texUp, 2); //передача текстурных координат в шейдер
 
    shader_program.enableAttributeArray(vertexLocation); //включить массив вершин, переданный в шейдер
    shader_program.enableAttributeArray(normLoc); //включить массив нормалей, переданный в шейдер
    shader_program.enableAttributeArray(texLoc); //включить текстурные координаты, переданный в шейдер
 
 
    glDrawArrays(GL_QUADS,0 ,2*4); //отрисовка
 
    shader_program.disableAttributeArray(vertexLocation); //выключение массива вершин
 
    shader_program.release(); //отрисовка всего, что передали в шейдер
}
 
void MainGLWidget::glSun()
{
    shader_program.bind(); //активация шейдера
    QMatrix4x4 ballMatr; //матрица для перемещения
    ballMatr.setToIdentity(); //идентифицировать матрицу еденицами
    ballMatr.rotate(sunAngle, 2, 0.5, 0); //поворот
    ballMatr.translate(-EARTH_SIZE/2.,EARTH_SIZE*2,0); //перенос на нужную позицию
 
    //передача значений переменных в шейдер
    //defCLoc - айдишник переменной в шейдер
    shader_program.setUniformValue(defCLoc, true);
    shader_program.setUniformValue(defAlways, true);
 
 
    //matrixLocation - айдишник переменной внутри шейдера,
    //projectMatrix - матрица проектирования,
    //modelViewMatrix - матрица для положения сцены
    //ballMatr - для расположения солнца
    shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*ballMatr);
    shader_program.setUniformValue(colorLocation, QColor(255, 207, 72)); //передача цвета в шейдер
 
    GLUquadricObj *ball = gluNewQuadric();  //объект, в котором мы рисуем луну
 
    ballMatr.translate(EARTH_SIZE, -EARTH_SIZE*4,0); //координаты, на которые нужно перенести луну
    //matrixLocation - айдишник переменной внутри шейдера,
    //projectMatrix - матрица проектирования,
    //modelViewMatrix - матрица для положения сцены
    //ballMatr - для расположения луны
    shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*ballMatr);
    shader_program.setUniformValue(colorLocation, QColor(189, 208, 228)); //передача цвета шейдер
    gluSphere(ball, CUBE_GRAN*2., 64, 64); //отрисовка луны
 
    gluDeleteQuadric(ball); //удаляем объект, в котором рисовали
    shader_program.setUniformValue(defCLoc, false); //передаем значения в шейдер и изменяем true на false
    shader_program.release(); //отрисовка всего переданного в шейдер
}
 
void MainGLWidget::glTrees()
{
    shader_program.bind(); //включить шейдер
 
    shader_program.setUniformValue(defAlways, true); //передача значений переменных в шейдер
    GLUquadricObj *cylinder = gluNewQuadric(); //создаем объект, в котором будем рисовать
    QMatrix4x4 r_90;
    r_90.rotate(-90, 1, 0 ,0); //развернуть цилиндры в вертикальное положение
 
    for(auto tree:trees)
    {
        shader_program.setUniformValue(colorLocation, QColor(255, 136, 0)); //передача цвета в шейдер
        QMatrix4x4 position_m;
        position_m.translate(tree.second.x(),0,  tree.second.y()); //сдвинуть дерево на позицию, заданную в массиву
        //передача значений переменных в шейдер
        //matrixLocation - айдишник переменной внутри шейдера,
        //projectMatrix - матрица проектирования,
        //modelViewMatrix - матрица для положения сцены
        //position_m - сдвинуть на место, в котором должно стоять дерево
        //r_90 - развернуть вертикально
        shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*position_m*r_90);
 
        //normMatrLoc - матрица нормалей
        //position_m - сдвинуть на место, в котором должно стоять дерево
        //r_90 - развернуть вертикально
        shader_program.setUniformValue(normMatrLoc, position_m*r_90);
        //отрисовка цилиндра(ствола)
        gluCylinder(cylinder, 0.2f, 0.2f, tree.first, 64, 64);
 
        shader_program.setUniformValue(colorLocation, QColor(0, 255, 0)); //передача цвета в шейдер
        for (float i = 1.5; i<=tree.first;i+=0.5)
        {
            QMatrix4x4 h_m;
            h_m.translate(tree.second.x(), i, tree.second.y()); //сдвинуть конус на нужную позицию
 
            //передача значений переменных в шейдер
            //matrixLocation - айдишник переменной внутри шейдера,
            //projectMatrix - матрица проектирования,
            //modelViewMatrix - матрица для положения сцены
            //h_m - сдвинуть на место, в котором должно стоять дерево
            //r_90 - развирнуть вертикально
            shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*h_m*r_90);
            gluCylinder(cylinder, 0.5+0.3f*(tree.first-i), 0.0f, 0.5f, 64, 64); //отрисовать конус(иголочки)
        }
    }
    gluDeleteQuadric(cylinder); //удаляем объект, при помощи которого рисовали
 
    shader_program.release(); //отрисовка всего, переданного в шейдер
}
 
void MainGLWidget::glCloud()
{
    shader_program.bind(); //включить обработку шейдером
    upLight(); //включить освещение
    QMatrix4x4 ballMatr; //позиция сферы в облаке
    shader_program.setUniformValue(colorLocation, QColor(30, 144, 255)); //передать цвет облака в шейдер
    GLUquadricObj *ball = gluNewQuadric(); //объект для отрисовки сферы
    shader_program.setUniformValue(defAlways, true); //цвет по умолчанию для сфер
    QMatrix4x4 c_m; //позиция всего облака
    c_m.setToIdentity(); //сделать матрицу единичной
    c_m.translate(cloudP.x(), 0, cloudP.y()); //перемещаем облако на нужную позицию
 
    float z = 2;
    for (int i=0;i<10;++i) //отрисовка ближней части облака
    {
        float x = -z*z+4; //парабола, чтобы облако было овальным, х - вправо/влево, z - ближе/дальше
        ballMatr.setToIdentity(); //сделать матрицу единичной
        ballMatr.translate(x,EARTH_SIZE,z); //переместить облако на нужную позицию
        shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*c_m*ballMatr); //передать матрицу преобразований в шейдер
        shader_program.setUniformValue(normMatrLoc, c_m*ballMatr); //матрица преобразования нормалей
        gluSphere(ball, CUBE_GRAN+0.25*(rand()%4), 64, 64); //отрисовка сферы, при каждой отрисовки грань куба будет от 1 до 1.75(для вида перемещения)
        z-=0.45; //шаг по параболе
    }
    z = -2;
    for (int i=0;i<10;++i) //отрисовка дальней части облака
    {
        float x = z*z-4; //парабола, чтобы облако было овальным, х - вправо/влево, z - ближе/дальше
        ballMatr.setToIdentity(); //сделать матрицу единичной
        ballMatr.translate(x,EARTH_SIZE,z); //переместить облако на нужную позицию
        shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*c_m*ballMatr); //передать матрицу преобразований в шейдер
        shader_program.setUniformValue(normMatrLoc, c_m*ballMatr); //матрица преобразования нормалей
        gluSphere(ball, CUBE_GRAN+0.25*(rand()%4), 64, 64); //отрисовка сферы, при каждой отрисовки грань куба будет от 1 до 1.75(для вида перемещения)
        z+=0.45; //шаг по параболе
    }
 
    for (int i=-2;i<3;++i) //верхняя часть облака по х(ближе/дальше)
    {
        for (int j=-1;j<2;++j) //верхняя часть облака по z(левее/правее)
        {
            if(cloudE[i+j+3]) //если cloudE[i] = 1, то нарисовать сферу из облака, иначе нет
            {
                ballMatr.setToIdentity(); //сделать матрицу единичной
                ballMatr.translate(i,EARTH_SIZE+j,0); //переместить облако на нужную позицию
                shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*c_m*ballMatr); //передать матрицу преобразований в шейдер
                shader_program.setUniformValue(normMatrLoc, c_m*ballMatr); //матрица преобразования нормалей
                gluSphere(ball, CUBE_GRAN+0.25*(rand()%4), 64, 64); //отрисовка сферы, при каждой отрисовки грань куба будет от 1 до 1.75(для вида перемещения)
            }
        }
    }
 
    gluDeleteQuadric(ball); //удалить объект для отрисовки
 
    shader_program.release(); //вызывает отрисовку шейдеров
}
 
void MainGLWidget::glDrop()
{
    for (int i=0;i<5; ++i) //на каждом шаге добавляется по 5 капелек
    {
        dropVector.push_back({cloudP.x()+1*(rand()%5-2), EARTH_SIZE, cloudP.y()+1*(rand()%5-2)}); //внутри облака капельки разбросаны рандомно
    }
 
    shader_program.bind(); //включить шейдер
    shader_program.setUniformValue(defAlways, true); //передать параметры в шейдерер, чтобы грани не закрашивались
    QMatrix4x4 dropMatr;
    QVector<QVector3D> newVec;
 
    shader_program.setUniformValue(colorLocation, QColor(0, 149,182)); //передать цвет в шейдер
    GLUquadricObj *ball = gluNewQuadric(); //объект, в котором рисуем
 
    for (auto &drop: dropVector)
    {
 
        dropMatr.setToIdentity(); //идентифицировать матрицу единицами
        dropMatr.translate(drop.x(), drop.y(), drop.z()); //переместить в нужное положение
        //передача значений переменных в шейдер
        //matrixLocation - айдишник переменной внутри шейдера,
        //projectMatrix - матрица проектирования,
        //modelViewMatrix - матрица для положения сцены
        //dropMatr - матрица капель
        shader_program.setUniformValue(matrixLocation, projectMatrix*modelViewMatrix*dropMatr);
        gluSphere(ball, 0.15, 64, 64); //отрисовка сфер (капель)
        QVector3D v;
 
        drop.setY(drop.y()-1); //чтобы по координате y сметится ниже(капелька падает)
        if (drop.y()>0.) //если еще не достигнута земля
            newVec.push_back(drop); //то записываем капельки, которые не достигли земли, в новый вектор
    }
    dropVector = newVec;
    gluDeleteQuadric(ball); //удалить объект для отрисовки
 
    shader_program.release(); //отрисовать все, что передали в шейдер
}
 
void MainGLWidget::stepCloud()
{
    if(fabs(cloudP.x()) > 10.) //чтобы облако не вылезало за границы земли
        cloudS.setX(-cloudS.x()); //если координата больше, чем нужно, то поменять направление на противоположное
    if(fabs(cloudP.y()) > 10.) //чтобы облако не вылезало за границы земли
        cloudS.setY(-cloudS.y()); //если координата больше, чем нужно, то поменять направление на противоположное
    cloudP.setX(cloudS.x()+cloudP.x()); //изменение координат облака
    cloudP.setY(cloudS.y()+cloudP.y());
}
 
void MainGLWidget::upLight()
{
    QMatrix4x4 ballMatr;
    ballMatr.setToIdentity(); //идентифицировать матрицу единицами
    ballMatr.rotate(sunAngle, 1, 0, 0); //точечный источник света(солнце)
    ballMatr.translate(0,EARTH_SIZE*2,0); //вынести из центра сцена на удаление
    QVector4D vec(0.0, 0.0, -3.0, 1); //позиция источника света
    vec = ballMatr*vec; //перенос позиции источника света
    shader_program.setUniformValue(lightPos, vec); //передать в шейдер позицию света
}
 
void MainGLWidget::run() //основной цикл
{
    using namespace std::chrono;
    high_resolution_clock::time_point t1, t2;
    t1 = high_resolution_clock::now();
    duration<double, std::milli> time_span; //переменная, чтобы получить разность времени
    while(1)
    {
        t2 = high_resolution_clock::now();
        time_span = t2 - t1;
        while(time_span.count()<100)
        {
            QCoreApplication::processEvents(); //прокручивает очередь событий
            t2 = high_resolution_clock::now();
            time_span = t2 - t1;
        }
        QCoreApplication::processEvents();
        stepCloud(); //движение облака
        sunAngle += angleSpeed*2; //угол солцне
        updateGL(); //перерисовка
        t1 = t2; //следующий кадр
    }
}
 
Содержание fragmentShader.fsh
uniform vec4 color;
uniform bool defAlways;
uniform vec4 lightPos;
uniform bool defC;
 
varying vec4 fragPos;
varying vec4 fnormal;
varying vec2 fragTex;
 
uniform sampler2D Tex;
void main(void)
{
    float ambientStrength = 0.1;
 
    float specularStrength = 0.1;
    vec3 lightColor = vec3(0.4, 0.3, 0.2);
    vec4 ambient = vec4(ambientStrength * lightColor,1);
    vec4 lightDir = normalize(lightPos - fragPos);
    float power = max(dot(fnormal, lightDir), 0.0);
    vec4 diffuse = vec4(power * lightColor, 1);
 
    vec4 texColor = texture2D(Tex, fragTex);
    vec4 sumLight = ambient+diffuse;
 
    gl_FragColor = color*sumLight*texColor; // Цвет по умолчанию
    if (fragPos.y == 1.0)
        gl_FragColor = vec4(0.247, 0.6078, 0.043137, 1)*sumLight*texColor;
    if (abs(fragPos.y) > 0.95)
    	if (abs(fragPos.x) > 0.95)
           gl_FragColor = vec4(0.0, 0.0, 0.0, 1)*sumLight*texColor;
 
    if (abs(fragPos.y) > 0.95)
    	if (abs(fragPos.z) > 0.95)
           gl_FragColor = vec4(0.0, 0.0, 0.0, 1)*sumLight*texColor;
 
    if (abs(fragPos.z) > 0.95)
    	if (abs(fragPos.x) > 0.95)
           gl_FragColor = vec4(0.0, 0.0, 0.0, 1)*sumLight*texColor;
 
    if (defAlways)
        gl_FragColor = color*sumLight*texColor; // Цвет по умолчанию
    if (defC)
        gl_FragColor = color*texColor;
 
}
 
Содержание vertexShader.vsh
attribute vec4 vertex;
attribute vec4 normal;
attribute vec2 texture;
 
uniform mat4 matrix;
uniform mat4 normalMatrix;
 
varying vec4 fragPos;
varying vec4 fnormal;
varying vec2 fragTex;
void main(void)
{
   gl_Position = matrix * vertex; //итоговая позиция точки
   fragPos = vertex;
   fnormal = normalMatrix*normal;
   fragTex = texture;
}
 
Содержание main.cpp
#include <QApplication>
#include "mainglwidget.h"
#include "mainwindow.h"
#include <time.h>
 
int main(int argc, char *argv[])
{
    srand(time(nullptr));
    QApplication a(argc, argv);
    MainGLWidget w(nullptr);
    w.show();
    w.run();
    return a.exec();
}