Среда программирования:
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(); }
Комментарии
Очень интересно! ии сайт крутой жалко что умирает(