From 7b790d20156d05e90a6c5e0150680c89e65d92ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sat, 21 Dec 2013 23:48:54 +0100 Subject: [PATCH] Switched opengl skybox to shaders --- src/basics/basics_global.h | 3 + src/definition/definition_global.h | 1 + src/render/opengl/ExplorerChunkSky.cpp | 148 ---------- src/render/opengl/ExplorerChunkSky.h | 42 --- src/render/opengl/OpenGLPart.cpp | 109 +++++++ src/render/opengl/OpenGLPart.h | 46 +++ src/render/opengl/OpenGLRenderer.cpp | 17 ++ src/render/opengl/OpenGLRenderer.h | 11 + src/render/opengl/OpenGLShaderProgram.cpp | 277 ++++++++++++++++++ src/render/opengl/OpenGLShaderProgram.h | 61 ++++ src/render/opengl/OpenGLSkybox.cpp | 96 ++++++ src/render/opengl/OpenGLSkybox.h | 35 +++ src/render/opengl/WidgetExplorer.cpp | 17 +- src/render/opengl/opengl.pro | 19 +- src/render/opengl/opengl_global.h | 2 + src/render/opengl/shaders/resources.qrc | 6 + src/render/opengl/shaders/skybox.frag | 233 +++++++++++++++ src/render/opengl/shaders/skybox.vert | 10 + .../software/AtmosphereModelBruneton.cpp | 15 + src/render/software/AtmosphereModelBruneton.h | 5 + src/render/software/AtmosphereRenderer.h | 2 + 21 files changed, 948 insertions(+), 207 deletions(-) delete mode 100644 src/render/opengl/ExplorerChunkSky.cpp delete mode 100644 src/render/opengl/ExplorerChunkSky.h create mode 100644 src/render/opengl/OpenGLPart.cpp create mode 100644 src/render/opengl/OpenGLPart.h create mode 100644 src/render/opengl/OpenGLShaderProgram.cpp create mode 100644 src/render/opengl/OpenGLShaderProgram.h create mode 100644 src/render/opengl/OpenGLSkybox.cpp create mode 100644 src/render/opengl/OpenGLSkybox.h create mode 100644 src/render/opengl/shaders/resources.qrc create mode 100644 src/render/opengl/shaders/skybox.frag create mode 100644 src/render/opengl/shaders/skybox.vert diff --git a/src/basics/basics_global.h b/src/basics/basics_global.h index e6830b6..853908d 100644 --- a/src/basics/basics_global.h +++ b/src/basics/basics_global.h @@ -20,6 +20,9 @@ namespace basics { class NoiseGenerator; class Curve; class ColorProfile; + class Texture2D; + class Texture3D; + class Texture4D; } } using namespace paysages::basics; diff --git a/src/definition/definition_global.h b/src/definition/definition_global.h index 7279f08..78585e3 100644 --- a/src/definition/definition_global.h +++ b/src/definition/definition_global.h @@ -2,6 +2,7 @@ #define DEFINITION_GLOBAL_H #include + #if defined(DEFINITION_LIBRARY) # define DEFINITIONSHARED_EXPORT Q_DECL_EXPORT #else diff --git a/src/render/opengl/ExplorerChunkSky.cpp b/src/render/opengl/ExplorerChunkSky.cpp deleted file mode 100644 index 3ebd5a9..0000000 --- a/src/render/opengl/ExplorerChunkSky.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "ExplorerChunkSky.h" - -#include -#include -#include "CameraDefinition.h" -#include "SoftwareRenderer.h" -#include "AtmosphereRenderer.h" -#include "AtmosphereResult.h" - -ExplorerChunkSky::ExplorerChunkSky(SoftwareRenderer* renderer, double size, SkyboxOrientation orientation) : BaseExplorerChunk(renderer) -{ - _box_size = size; - _orientation = orientation; - _center = VECTOR_ZERO; - - setMaxTextureSize(256); - maintain(); -} - -void ExplorerChunkSky::onCameraEvent(CameraDefinition* camera) -{ - _center = camera->getLocation(); -} - -void ExplorerChunkSky::onRenderEvent(QGLWidget*) -{ - double size = _box_size; - Vector3 camera = _center; - - glBegin(GL_QUADS); - switch (_orientation) - { - case SKYBOX_NORTH: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z - size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z - size); - break; - case SKYBOX_SOUTH: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z + size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z + size); - break; - case SKYBOX_EAST: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z - size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z + size); - break; - case SKYBOX_WEST: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z + size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z - size); - break; - case SKYBOX_TOP: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z + size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y + size, camera.z - size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y + size, camera.z - size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z + size); - break; - case SKYBOX_BOTTOM: - /*glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y - size, camera.z - size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y - size, camera.z - size);*/ - break; - } - glEnd(); -} - -double ExplorerChunkSky::getDisplayedSizeHint(CameraDefinition*) -{ - return 1000.0; -} - -Color ExplorerChunkSky::getTextureColor(double x, double y) -{ - Vector3 location; - - x -= 0.5; - y -= 0.5; - - switch (_orientation) - { - case SKYBOX_NORTH: - location.x = x; - location.y = -y; - location.z = -0.5; - break; - case SKYBOX_SOUTH: - location.x = -x; - location.y = -y; - location.z = 0.5; - break; - case SKYBOX_EAST: - location.x = 0.5; - location.y = -y; - location.z = x; - break; - case SKYBOX_WEST: - location.x = -0.5; - location.y = -y; - location.z = -x; - break; - case SKYBOX_TOP: - location.x = x; - location.y = 0.5; - location.z = -y; - break; - case SKYBOX_BOTTOM: - location.x = x; - location.y = -0.5; - location.z = y; - break; - } - location = location.normalize(); - if (location.y < 0.0) - { - location.y = 0.0; - } - return renderer()->getAtmosphereRenderer()->getSkyColor(location).final; -} diff --git a/src/render/opengl/ExplorerChunkSky.h b/src/render/opengl/ExplorerChunkSky.h deleted file mode 100644 index 7948d79..0000000 --- a/src/render/opengl/ExplorerChunkSky.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef EXPLORERCHUNKSKY_H -#define EXPLORERCHUNKSKY_H - -#include "opengl_global.h" - -#include "BaseExplorerChunk.h" - -#include "Vector3.h" - -namespace paysages { -namespace opengl { - -enum SkyboxOrientation -{ - SKYBOX_NORTH, - SKYBOX_SOUTH, - SKYBOX_WEST, - SKYBOX_EAST, - SKYBOX_TOP, - SKYBOX_BOTTOM -}; - -class OPENGLSHARED_EXPORT ExplorerChunkSky:public BaseExplorerChunk -{ -public: - ExplorerChunkSky(SoftwareRenderer* renderer, double size, SkyboxOrientation orientation); - - void onCameraEvent(CameraDefinition* camera); - void onRenderEvent(QGLWidget* widget); - double getDisplayedSizeHint(CameraDefinition* camera); - Color getTextureColor(double x, double y); - -private: - SkyboxOrientation _orientation; - double _box_size; - Vector3 _center; -}; - -} -} - -#endif // EXPLORERCHUNKSKY_H diff --git a/src/render/opengl/OpenGLPart.cpp b/src/render/opengl/OpenGLPart.cpp new file mode 100644 index 0000000..c2d6d98 --- /dev/null +++ b/src/render/opengl/OpenGLPart.cpp @@ -0,0 +1,109 @@ +#include "OpenGLPart.h" + +#include +#include +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "CameraDefinition.h" +#include "AtmosphereDefinition.h" +#include "AtmosphereRenderer.h" +#include "WaterRenderer.h" +#include "Scenery.h" + +OpenGLPart::OpenGLPart(OpenGLRenderer* renderer): + renderer(renderer) +{ +} + +OpenGLPart::~OpenGLPart() +{ + QMapIterator i(shaders); + while (i.hasNext()) + { + i.next(); + delete i.value(); + } +} + +OpenGLShaderProgram* OpenGLPart::createShader(QString name) +{ + OpenGLShaderProgram* program = new OpenGLShaderProgram(name, renderer->getOpenGlFunctions()); + + if (!shaders.contains(name)) + { + shaders[name] = program; + return program; + } + else + { + return 0; + } +} + +void OpenGLPart::postInitialize() +{ + QMapIterator i(shaders); + while (i.hasNext()) + { + i.next(); + i.value()->compile(); + } +} + +void OpenGLPart::updateCamera(CameraDefinition* camera) +{ + // Get camera info + Vector3 location = camera->getLocation(); + Vector3 target = camera->getTarget(); + Vector3 up = camera->getUpVector(); + CameraPerspective perspective = camera->getPerspective(); + + QVector3D vlocation(location.x, location.y, location.z); + + // Compute matrix + QMatrix4x4 transform; + transform.setToIdentity(); + transform.lookAt(vlocation, + QVector3D(target.x, target.y, target.z), + QVector3D(up.x, up.y, up.z)); + + QMatrix4x4 projection; + projection.setToIdentity(); + projection.perspective(perspective.yfov * 180.0 / M_PI, perspective.xratio, perspective.znear, perspective.zfar); + + // Set in shaders + QMapIterator i(shaders); + while (i.hasNext()) + { + i.next(); + i.value()->updateCamera(vlocation, projection * transform); + } +} + +void OpenGLPart::updateScenery(bool onlyCommon) +{ + Scenery* scenery = renderer->getScenery(); + + // Collect common info + double water_height = renderer->getWaterRenderer()->getHeightInfo().max_height; + Vector3 orig_sun_direction = renderer->getAtmosphereRenderer()->getSunDirection(); + QVector3D sun_direction = QVector3D(orig_sun_direction.x, orig_sun_direction.y, orig_sun_direction.z); + Color orig_sun_color = scenery->getAtmosphere()->sun_color; + QColor sun_color = QColor(orig_sun_color.r, orig_sun_color.g, orig_sun_color.b); + + // Update shaders + QMapIterator i(shaders); + while (i.hasNext()) + { + i.next(); + + i.value()->updateWaterHeight(water_height); + i.value()->updateSun(sun_direction, sun_color); + } + + // Let subclass do its own collecting + if (not onlyCommon) + { + update(); + } +} diff --git a/src/render/opengl/OpenGLPart.h b/src/render/opengl/OpenGLPart.h new file mode 100644 index 0000000..b7ae211 --- /dev/null +++ b/src/render/opengl/OpenGLPart.h @@ -0,0 +1,46 @@ +#ifndef OPENGLPART_H +#define OPENGLPART_H + +#include "opengl_global.h" + +#include +#include + +namespace paysages { +namespace opengl { + +// Class that can be inherited by scenery parts, to use OpenGL features + +class OPENGLSHARED_EXPORT OpenGLPart +{ +public: + OpenGLPart(OpenGLRenderer* renderer); + virtual ~OpenGLPart(); + + // Initialize the part rendering (create shaders, prepare static textures...) + virtual void initialize() = 0; + + // Update parameters from scenery + virtual void update() = 0; + + // Do the rendering + virtual void render() = 0; + + void postInitialize(); + void updateCamera(CameraDefinition* camera); + void updateScenery(bool onlyCommon=false); + +protected: + // Create a shader program + OpenGLShaderProgram* createShader(QString name); + + // Access to the main scenery renderer + OpenGLRenderer* renderer; + +private: + QMap shaders; +}; +} +} + +#endif // OPENGLPART_H diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 31bbfc6..0106179 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -1,18 +1,22 @@ #include "OpenGLRenderer.h" +#include #include #include #include #include "Scenery.h" #include "CameraDefinition.h" +#include "OpenGLSkybox.h" OpenGLRenderer::OpenGLRenderer(Scenery* scenery): SoftwareRenderer(scenery) { + skybox = new OpenGLSkybox(this); } OpenGLRenderer::~OpenGLRenderer() { + delete skybox; } void OpenGLRenderer::initialize() @@ -38,6 +42,11 @@ void OpenGLRenderer::initialize() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); prepare(); + + functions = new QOpenGLFunctions(); + + skybox->initialize(); + skybox->updateScenery(); } void OpenGLRenderer::resize(int width, int height) @@ -56,7 +65,15 @@ void OpenGLRenderer::resize(int width, int height) void OpenGLRenderer::paint() { + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + skybox->render(); +} + +void OpenGLRenderer::cameraChangeEvent(CameraDefinition *camera) +{ + skybox->updateCamera(camera); } double OpenGLRenderer::getPrecision(const Vector3 &) diff --git a/src/render/opengl/OpenGLRenderer.h b/src/render/opengl/OpenGLRenderer.h index 075b3ac..fc5786c 100644 --- a/src/render/opengl/OpenGLRenderer.h +++ b/src/render/opengl/OpenGLRenderer.h @@ -5,6 +5,8 @@ #include "SoftwareRenderer.h" +class QOpenGLFunctions; + namespace paysages { namespace opengl { @@ -21,8 +23,17 @@ public: void resize(int width, int height); void paint(); + void cameraChangeEvent(CameraDefinition* camera); + + inline QOpenGLFunctions* getOpenGlFunctions() {return functions;} + virtual double getPrecision(const Vector3 &location) override; virtual Color applyMediumTraversal(Vector3 location, Color color) override; + +private: + QOpenGLFunctions* functions; + + OpenGLSkybox* skybox; }; } diff --git a/src/render/opengl/OpenGLShaderProgram.cpp b/src/render/opengl/OpenGLShaderProgram.cpp new file mode 100644 index 0000000..f9e11bb --- /dev/null +++ b/src/render/opengl/OpenGLShaderProgram.cpp @@ -0,0 +1,277 @@ +#include "OpenGLShaderProgram.h" + +#include +#include +#include +#include "Texture2D.h" +#include "Texture3D.h" +#include "Texture4D.h" +#include "Color.h" + +OpenGLShaderProgram::OpenGLShaderProgram(QString name, QOpenGLFunctions* functions): + name(name), functions(functions) +{ + program = new QOpenGLShaderProgram(); +} + +OpenGLShaderProgram::~OpenGLShaderProgram() +{ + delete program; +} + +void OpenGLShaderProgram::addVertexSource(QString path) +{ + program->addShaderFromSourceFile(QOpenGLShader::Vertex, QString(":/shaders/%1.vert").arg(path)); +} + +void OpenGLShaderProgram::addFragmentSource(QString path) +{ + program->addShaderFromSourceFile(QOpenGLShader::Fragment, QString(":/shaders/%1.frag").arg(path)); +} + +void OpenGLShaderProgram::compile() +{ + if (not program->link()) + { + qWarning() << "Error while compiling shader " << name << "\n" << program->log() << "\n"; + } + else + { + qDebug() << "Shader " << name << " compilation output:\n" << program->log() << "\n"; + } +} + +void OpenGLShaderProgram::updateCamera(const QVector3D& location, const QMatrix4x4& view) +{ + this->camera_location = location; + this->view = view; +} + +void OpenGLShaderProgram::updateWaterHeight(double height) +{ + this->water_height = height; +} + +void OpenGLShaderProgram::updateSun(const QVector3D& direction, const QColor& color) +{ + this->sun_direction = direction; + this->sun_color = color; +} + +void OpenGLShaderProgram::addTexture(QString sampler_name, Texture2D* texture) +{ + GLuint texid; + + if (textures.contains(sampler_name)) + { + texid = textures[sampler_name].second; + } + else + { + glGenTextures(1, &texid); + textures[sampler_name] = QPair(GL_TEXTURE_2D, texid); + } + + glBindTexture(GL_TEXTURE_2D, texid); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + int sx, sy; + texture->getSize(&sx, &sy); + float* pixels = new float[sx * sy * 4]; + for (int x = 0; x < sx; x++) + { + for (int y = 0; y < sy; y++) + { + float* pixel = pixels + (y * sx + x) * 4; + Color col = texture->getPixel(x, y); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sx, sy, 0, GL_RGBA, GL_FLOAT, pixels); + delete[] pixels; +} + +void OpenGLShaderProgram::addTexture(QString sampler_name, Texture3D* texture) +{ + GLuint texid; + + if (textures.contains(sampler_name)) + { + texid = textures[sampler_name].second; + } + else + { + glGenTextures(1, &texid); + textures[sampler_name] = QPair(GL_TEXTURE_3D, texid); + } + + glBindTexture(GL_TEXTURE_3D, texid); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + int sx, sy, sz; + texture->getSize(&sx, &sy, &sz); + float* pixels = new float[sx * sy * sz * 4]; + for (int x = 0; x < sx; x++) + { + for (int y = 0; y < sy; y++) + { + for (int z = 0; z < sz; z++) + { + float* pixel = pixels + (z * (sx * sy) + y * sx + x) * 4; + Color col = texture->getPixel(x, y, z); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + } + + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, sx, sy, sz, 0, GL_RGBA, GL_FLOAT, pixels); + delete[] pixels; +} + +void OpenGLShaderProgram::addTexture(QString sampler_name, Texture4D* texture) +{ + GLuint texid; + + if (textures.contains(sampler_name)) + { + texid = textures[sampler_name].second; + } + else + { + glGenTextures(1, &texid); + textures[sampler_name] = QPair(GL_TEXTURE_3D, texid); + } + + glBindTexture(GL_TEXTURE_3D, texid); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + int sx, sy, sz, sw; + texture->getSize(&sx, &sy, &sz, &sw); + float* pixels = new float[sx * sy * sz * sw * 4]; + for (int x = 0; x < sx; x++) + { + for (int y = 0; y < sy; y++) + { + for (int z = 0; z < sz; z++) + { + for (int w = 0; w < sw; w++) + { + float* pixel = pixels + (w * (sx * sy * sz) + z * (sx * sy) + y * sx + x) * 4; + Color col = texture->getPixel(x, y, z, w); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + } + } + + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, sx, sy, sz * sw, 0, GL_RGBA, GL_FLOAT, pixels); + delete[] pixels; +} + +void OpenGLShaderProgram::bind() +{ + program->bind(); + + // TODO Keep locations in cache + + int viewMatrix = program->uniformLocation("viewMatrix"); + if (viewMatrix >= 0) + { + program->setUniformValue(viewMatrix, view); + } + + int cameraLocation = program->uniformLocation("cameraLocation"); + if (cameraLocation >= 0) + { + program->setUniformValue(cameraLocation, camera_location); + } + + int waterHeight = program->uniformLocation("waterHeight"); + if (waterHeight >= 0) + { + program->setUniformValue(waterHeight, water_height); + } + + int sunDirection = program->uniformLocation("sunDirection"); + if (sunDirection >= 0) + { + program->setUniformValue(sunDirection, sun_direction); + } + + int sunColor = program->uniformLocation("sunColor"); + if (sunColor >= 0) + { + program->setUniformValue(sunColor, sun_color); + } + + QMapIterator > iter(textures); + int i = 0; + while (iter.hasNext()) + { + iter.next(); + int textureSampler = program->uniformLocation(iter.key()); + if (textureSampler >= 0) + { + glActiveTexture(GL_TEXTURE0 + i); + glBindTexture(iter.value().first, iter.value().second); + program->setUniformValue(textureSampler, i); + i++; + } + } +} + +void OpenGLShaderProgram::release() +{ + program->release(); +} + +void OpenGLShaderProgram::drawTriangles(float* vertices, int triangle_count) +{ + bind(); + + GLuint vertex = program->attributeLocation("vertex"); + program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); + program->enableAttributeArray(vertex); + + glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3); + + program->disableAttributeArray(vertex); + + release(); +} + +void OpenGLShaderProgram::drawTriangleStrip(float* vertices, int vertex_count) +{ + bind(); + + GLuint vertex = program->attributeLocation("vertex"); + program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); + program->enableAttributeArray(vertex); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); + + program->disableAttributeArray(vertex); + + release(); +} diff --git a/src/render/opengl/OpenGLShaderProgram.h b/src/render/opengl/OpenGLShaderProgram.h new file mode 100644 index 0000000..aa8e30a --- /dev/null +++ b/src/render/opengl/OpenGLShaderProgram.h @@ -0,0 +1,61 @@ +#ifndef OPENGLSHADERPROGRAM_H +#define OPENGLSHADERPROGRAM_H + +#include "opengl_global.h" + +#include +#include +#include +#include +#include + +class QOpenGLShaderProgram; +class QOpenGLFunctions; + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLShaderProgram +{ +public: + OpenGLShaderProgram(QString name, QOpenGLFunctions* functions); + ~OpenGLShaderProgram(); + + void addVertexSource(QString path); + void addFragmentSource(QString path); + void compile(); + + void updateCamera(const QVector3D& location, const QMatrix4x4& view); + void updateWaterHeight(double height); + void updateSun(const QVector3D& direction, const QColor& color); + + void addTexture(QString sampler_name, Texture2D* texture); + void addTexture(QString sampler_name, Texture3D* texture); + void addTexture(QString sampler_name, Texture4D* texture); + + void drawTriangles(float* vertices, int triangle_count); + void drawTriangleStrip(float* vertices, int vertex_count); + +private: + void bind(); + void release(); + + QMatrix4x4 view; + QVector3D camera_location; + + float water_height; + + QVector3D sun_direction; + QColor sun_color; + + QString name; + QOpenGLShaderProgram* program; + QOpenGLFunctions* functions; + + QMap > textures; +}; + +} +} + +#endif // OPENGLSHADERPROGRAM_H diff --git a/src/render/opengl/OpenGLSkybox.cpp b/src/render/opengl/OpenGLSkybox.cpp new file mode 100644 index 0000000..8cfa29a --- /dev/null +++ b/src/render/opengl/OpenGLSkybox.cpp @@ -0,0 +1,96 @@ +#include "OpenGLSkybox.h" + +#include +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "Scenery.h" +#include "AtmosphereDefinition.h" +#include "AtmosphereRenderer.h" +#include "AtmosphereModelBruneton.h" + +OpenGLSkybox::OpenGLSkybox(OpenGLRenderer* renderer): + OpenGLPart(renderer) +{ + vertices = new float[14 * 3]; + daytime = renderer->getScenery()->getAtmosphere()->_daytime; +} + +OpenGLSkybox::~OpenGLSkybox() +{ + delete[] vertices; +} + +void OpenGLSkybox::initialize() +{ + program = createShader("skybox"); + program->addVertexSource("skybox"); + program->addFragmentSource("skybox"); + + setVertex(0, 1.0f, 1.0f, 1.0f); + setVertex(12, 1.0f, 1.0f, 1.0f); + + setVertex(1, 1.0f, -1.0f, 1.0f); + setVertex(5, 1.0f, -1.0f, 1.0f); + setVertex(13, 1.0f, -1.0f, 1.0f); + + setVertex(2, -1.0f, 1.0f, 1.0f); + setVertex(10, -1.0f, 1.0f, 1.0f); + + setVertex(3, -1.0f, -1.0f, 1.0f); + + setVertex(4, -1.0f, -1.0f, -1.0f); + setVertex(8, -1.0f, -1.0f, -1.0f); + + setVertex(6, 1.0f, -1.0f, -1.0f); + + setVertex(7, 1.0f, 1.0f, -1.0f); + setVertex(11, 1.0f, 1.0f, -1.0f); + + setVertex(9, -1.0f, 1.0f, -1.0f); +} + +void OpenGLSkybox::update() +{ + SoftwareBrunetonAtmosphereRenderer* bruneton = (SoftwareBrunetonAtmosphereRenderer*)renderer->getAtmosphereRenderer(); + + program->addTexture("transmittanceTexture", bruneton->getModel()->getTextureTransmittance()); + program->addTexture("inscatterTexture", bruneton->getModel()->getTextureInscatter()); +} + +void OpenGLSkybox::render() +{ + program->drawTriangleStrip(vertices, 14); +} + +void OpenGLSkybox::alterDayTime(double delta) +{ +#if 0 + Scenery* scenery = renderer->getScenery(); + AtmosphereDefinition* definition = scenery->getAtmosphere()->definition; + daytime = fmod(daytime + delta * 0.001, 1.0); + // TEMP + if (daytime > 0.8) + { + daytime -= 0.6; + } + if (daytime < 0.2) + { + daytime += 0.6; + } + + definition->hour = (int)(daytime * 24.0); + definition->minute = (int)((daytime - (((double)definition->hour) / 24.0)) * 1440.0); + + AtmosphereDefinitionClass.validate(definition); + + // TODO Update only the sun + updateScenery(scenery, true); +#endif +} + +void OpenGLSkybox::setVertex(int i, float x, float y, float z) +{ + vertices[i * 3] = x; + vertices[i * 3 + 1] = y; + vertices[i * 3 + 2] = z; +} diff --git a/src/render/opengl/OpenGLSkybox.h b/src/render/opengl/OpenGLSkybox.h new file mode 100644 index 0000000..e2a72cb --- /dev/null +++ b/src/render/opengl/OpenGLSkybox.h @@ -0,0 +1,35 @@ +#ifndef OPENGLSKYBOX_H +#define OPENGLSKYBOX_H + +#include "opengl_global.h" + +#include "OpenGLPart.h" + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLSkybox: public OpenGLPart +{ +public: + OpenGLSkybox(OpenGLRenderer* renderer); + virtual ~OpenGLSkybox(); + + virtual void initialize() override; + virtual void update() override; + virtual void render() override; + + void alterDayTime(double delta); + +private: + void setVertex(int i, float x, float y, float z); + + OpenGLShaderProgram* program; + float* vertices; + + double daytime; +}; + +} +} + +#endif // OPENGLSKYBOX_H diff --git a/src/render/opengl/WidgetExplorer.cpp b/src/render/opengl/WidgetExplorer.cpp index 2337813..b31d0ce 100644 --- a/src/render/opengl/WidgetExplorer.cpp +++ b/src/render/opengl/WidgetExplorer.cpp @@ -11,7 +11,6 @@ #include "WaterDefinition.h" #include "SurfaceMaterial.h" #include "CameraDefinition.h" -#include "ExplorerChunkSky.h" #include "ExplorerChunkTerrain.h" #include "TerrainRenderer.h" #include "WaterRenderer.h" @@ -113,14 +112,6 @@ void WidgetExplorer::startRendering() } } - // Add skybox - for (int orientation = 0; orientation < 5; orientation++) - { - ExplorerChunkSky* chunk = new ExplorerChunkSky(_renderer, 500.0, (SkyboxOrientation) orientation); - _chunks.append(chunk); - _updateQueue.append(chunk); - } - // Start rendering workers int nbcore; _alive = true; @@ -374,9 +365,13 @@ void WidgetExplorer::paintGL() // Don't do this at each frame, only on camera change _renderer->getScenery()->setCamera(_current_camera); _renderer->getScenery()->getCamera(_current_camera); + _renderer->cameraChangeEvent(_current_camera); start_time = QTime::currentTime(); + // Background + _renderer->paint(); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); Vector3 camera_location = _current_camera->getLocation(); @@ -384,10 +379,6 @@ void WidgetExplorer::paintGL() Vector3 camera_up = _current_camera->getUpVector(); gluLookAt(camera_location.x, camera_location.y, camera_location.z, camera_target.x, camera_target.y, camera_target.z, camera_up.x, camera_up.y, camera_up.z); - // Background - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Render water double water_height = _renderer->getTerrainRenderer()->getWaterHeight(); glDisable(GL_TEXTURE_2D); diff --git a/src/render/opengl/opengl.pro b/src/render/opengl/opengl.pro index cd7042e..5e47964 100644 --- a/src/render/opengl/opengl.pro +++ b/src/render/opengl/opengl.pro @@ -16,17 +16,21 @@ include(../../common.pri) SOURCES += \ OpenGLRenderer.cpp \ BaseExplorerChunk.cpp \ - ExplorerChunkSky.cpp \ ExplorerChunkTerrain.cpp \ - WidgetExplorer.cpp + WidgetExplorer.cpp \ + OpenGLShaderProgram.cpp \ + OpenGLPart.cpp \ + OpenGLSkybox.cpp HEADERS +=\ opengl_global.h \ OpenGLRenderer.h \ BaseExplorerChunk.h \ - ExplorerChunkSky.h \ ExplorerChunkTerrain.h \ - WidgetExplorer.h + WidgetExplorer.h \ + OpenGLShaderProgram.h \ + OpenGLPart.h \ + OpenGLSkybox.h unix:!symbian { maemo5 { @@ -60,3 +64,10 @@ else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../software/debug/ else:unix: LIBS += -L$$OUT_PWD/../software/ -lpaysages_render_software INCLUDEPATH += $$PWD/../software DEPENDPATH += $$PWD/../software + +RESOURCES += \ + shaders/resources.qrc + +OTHER_FILES += \ + shaders/skybox.frag \ + shaders/skybox.vert diff --git a/src/render/opengl/opengl_global.h b/src/render/opengl/opengl_global.h index 19fa590..63b65f6 100644 --- a/src/render/opengl/opengl_global.h +++ b/src/render/opengl/opengl_global.h @@ -16,6 +16,8 @@ namespace opengl { class WidgetExplorer; class OpenGLRenderer; class BaseExplorerChunk; + class OpenGLShaderProgram; + class OpenGLSkybox; } } using namespace paysages::opengl; diff --git a/src/render/opengl/shaders/resources.qrc b/src/render/opengl/shaders/resources.qrc new file mode 100644 index 0000000..a922c12 --- /dev/null +++ b/src/render/opengl/shaders/resources.qrc @@ -0,0 +1,6 @@ + + + skybox.frag + skybox.vert + + diff --git a/src/render/opengl/shaders/skybox.frag b/src/render/opengl/shaders/skybox.frag new file mode 100644 index 0000000..3ffa70e --- /dev/null +++ b/src/render/opengl/shaders/skybox.frag @@ -0,0 +1,233 @@ +const float GROUND_OFFSET = 0.5; +const float Rg = 6360.0; +const float Rt = 6420.0; +const float RL = 6421.0; +const float exposure = 0.4; +const float ISun = 100.0; +const float AVERAGE_GROUND_REFLECTANCE = 0.1; +const float HR = 8.0; +const vec3 betaR = vec3(5.8e-3, 1.35e-2, 3.31e-2); +const float HM = 1.2; +const vec3 betaMSca = vec3(4e-3); +const vec3 betaMEx = vec3(4e-3 / 0.9); +const float mieG = 0.8; +const float SPHERE_SIZE = 20000.0; +const float WORLD_SCALING = 0.05; +const float SUN_DISTANCE = 149597870.0; +const float SUN_DISTANCE_SCALED = (SUN_DISTANCE / WORLD_SCALING); +const float SUN_RADIUS = 6.955e5; +const float SUN_RADIUS_SCALED = (SUN_RADIUS / WORLD_SCALING); +const float M_PI = 3.141592657; + +const int RES_MU = 128; +const int RES_MU_S = 32; +const int RES_R = 32; +const int RES_NU = 8; + +uniform float waterHeight; +uniform vec3 cameraLocation; +uniform vec3 sunDirection; +uniform vec4 sunColor; +const float sunRadius = 1.0; // TODO -> uniform + +varying vec3 unprojected; + +uniform sampler2D transmittanceTexture; +uniform sampler3D inscatterTexture; + +vec4 texture4D(sampler3D tex, float r, float mu, float muS, float nu) +{ + if (r < Rg + 0.00000001) r = Rg + 0.00000001; + float H = sqrt(Rt * Rt - Rg * Rg); + float rho = sqrt(r * r - Rg * Rg); + float rmu = r * mu; + float delta = rmu * rmu - r * r + Rg * Rg; + vec4 cst = (rmu < 0.0 && delta > 0.0) ? vec4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(RES_MU)) : vec4(-1.0, H * H, H, 0.5 + 0.5 / float(RES_MU)); + float uR = 0.5 / float(RES_R) + rho / H * (1.0 - 1.0 / float(RES_R)); + float uMu = cst.a + (rmu * cst.r + sqrt(delta + cst.g)) / (rho + cst.b) * (0.5 - 1.0 / float(RES_MU)); + float uMuS = 0.5 / float(RES_MU_S) + (atan(max(muS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(RES_MU_S)); + + float sr = 1.0 / float(RES_R); + int br = int(floor(uR / sr)); + vec4 r1 = texture3D(tex, vec3(uMu, uMuS, float(br) * sr + nu * sr)); + vec4 r2 = texture3D(tex, vec3(uMu, uMuS, float(br + 1) * sr + nu * sr)); + return mix(r1, r2, (uR - float(br) * sr) / sr); +} + +float _limit(float r, float mu) +{ + float dout = -r * mu + sqrt(r * r * (mu * mu - 1.0) + RL * RL); + float delta2 = r * r * (mu * mu - 1.0) + Rg * Rg; + if (delta2 >= 0.0) + { + float din = -r * mu - sqrt(delta2); + if (din >= 0.0) { + dout = min(dout, din); + } + } + return dout; +} + +vec2 _getTransmittanceUV(float r, float mu) +{ + if (r < Rg + 0.00000001) r = Rg + 0.00000001; + float dr = (r - Rg) / (Rt - Rg); + return vec2(atan((mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5, sqrt(dr)); +} + +vec4 _transmittance(float r, float mu) +{ + vec2 uv = _getTransmittanceUV(r, mu); + return texture2D(transmittanceTexture, uv); +} + +vec4 _transmittanceWithShadow(float r, float mu) +{ + return mu < -sqrt(1.0 - (Rg / r) * (Rg / r)) ? vec4(0.0) : _transmittance(r, mu); +} + +vec4 _sunTransmittance(vec3 v, vec3 s, float r, float mu, float radius) +{ + vec4 transmittance = r <= Rt ? _transmittanceWithShadow(r, mu) : vec4(1.0); /* T(x,xo) */ + float d = _limit(r, mu); + radius *= (1.0 + 10.0 * d / Rt); /* Inflating due to lens effect near horizon */ + float isun = step(cos(radius * M_PI / 180.0), dot(v, s)) * ISun; /* Lsun */ + transmittance.r *= isun; + transmittance.g *= isun; + transmittance.b *= isun; + transmittance.a = 1.0; + return transmittance; /* Eq (9) */ +} + +float phaseFunctionR(float mu) { + return (3.0 / (16.0 * M_PI)) * (1.0 + mu * mu); +} + +float phaseFunctionM(float mu) { + return 1.5 * 1.0 / (4.0 * M_PI) * (1.0 - mieG*mieG) * pow(1.0 + (mieG*mieG) - 2.0*mieG*mu, -3.0/2.0) * (1.0 + mu * mu) / (2.0 + mieG*mieG); +} + +float opticalDepth(float H, float r, float mu, float d) { + float a = sqrt((0.5/H)*r); + vec2 a01 = a*vec2(mu, mu + d / r); + vec2 a01s = sign(a01); + vec2 a01sq = a01*a01; + float x = a01s.y > a01s.x ? exp(a01sq.x) : 0.0; + vec2 y = a01s / (2.3193*abs(a01) + sqrt(1.52*a01sq + 4.0)) * vec2(1.0, exp(-d/H*(d/(2.0*r)+mu))); + return sqrt((6.2831*H)*r) * exp((Rg-r)/H) * (x + dot(y, vec2(1.0, -1.0))); +} + +vec3 analyticTransmittance(float r, float mu, float d) { + return exp(- betaR * opticalDepth(HR, r, mu, d) - betaMEx * opticalDepth(HM, r, mu, d)); +} + +vec3 getMie(vec4 rayMie) { // rayMie.rgb=C*, rayMie.w=Cm,r + return rayMie.rgb * rayMie.w / max(rayMie.r, 1e-4) * (betaR.r / betaR); +} + +vec3 _getInscatterColor(inout vec3 x, inout float t, vec3 v, vec3 s, out float r, out float mu, out vec3 attenuation) { + vec3 result; + r = length(x); + mu = dot(x, v) / r; + float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + Rt * Rt); + if (d > 0.0) { // if x in space and ray intersects atmosphere + // move x to nearest intersection of ray with top atmosphere boundary + x += d * v; + t -= d; + mu = (r * mu + d) / Rt; + r = Rt; + } + if (r <= Rt) { // if ray intersects atmosphere + float nu = dot(v, s); + float muS = dot(x, s) / r; + float phaseR = phaseFunctionR(nu); + float phaseM = phaseFunctionM(nu); + vec4 inscatter = max(texture4D(inscatterTexture, r, mu, muS, nu), 0.0); + if (t > 0.0) { + vec3 x0 = x + t * v; + float r0 = length(x0); + float rMu0 = dot(x0, v); + float mu0 = rMu0 / r0; + float muS0 = dot(x0, s) / r0; + // avoids imprecision problems in transmittance computations based on textures + attenuation = analyticTransmittance(r, mu, t); + if (r0 > Rg + 0.001) { + // computes S[L]-T(x,x0)S[L]|x0 + inscatter = max(inscatter - attenuation.rgbr * texture4D(inscatterTexture, r0, mu0, muS0, nu), 0.0); + // avoids imprecision problems near horizon by interpolating between two points above and below horizon + const float EPS = 0.02; + float muHoriz = -sqrt(1.0 - (Rg / r) * (Rg / r)); + if (abs(mu - muHoriz) < EPS) { + float a = ((mu - muHoriz) + EPS) / (2.0 * EPS); + + mu = muHoriz - EPS; + r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); + mu0 = (r * mu + t) / r0; + vec4 inScatter0 = texture4D(inscatterTexture, r, mu, muS, nu); + vec4 inScatter1 = texture4D(inscatterTexture, r0, mu0, muS0, nu); + vec4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); + + mu = muHoriz + EPS; + r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); + mu0 = (r * mu + t) / r0; + inScatter0 = texture4D(inscatterTexture, r, mu, muS, nu); + inScatter1 = texture4D(inscatterTexture, r0, mu0, muS0, nu); + vec4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); + + inscatter = mix(inScatterA, inScatterB, a); + } + } + } + // avoids imprecision problems in Mie scattering when sun is below horizon + inscatter.w *= smoothstep(0.00, 0.02, muS); + result = max(inscatter.rgb * phaseR + getMie(inscatter) * phaseM, 0.0); + } else { // x in space and ray looking in space + result = vec3(0.0); + } + return result * ISun; +} + +float _uncharted2Tonemap(float x) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} + +vec4 _toneMappingUncharted(vec4 color, float exposure) +{ + float W = 11.2; + float white_scale = 1.0 / _uncharted2Tonemap(W); + vec4 result; + + result.r = pow(_uncharted2Tonemap(color.r * exposure) * white_scale, 1.0 / 2.2); + result.g = pow(_uncharted2Tonemap(color.g * exposure) * white_scale, 1.0 / 2.2); + result.b = pow(_uncharted2Tonemap(color.b * exposure) * white_scale, 1.0 / 2.2); + result.a = 1.0; + + return result; +} + +void main(void) +{ + float yoffset = GROUND_OFFSET - waterHeight; + float cameray = max(cameraLocation[1] + yoffset, 0.0); + vec3 x = vec3(0.0, Rg + cameray * WORLD_SCALING, 0.0); + vec3 v = normalize(unprojected - cameraLocation); + vec3 s = normalize(sunDirection * SUN_DISTANCE_SCALED - x); + + float r = length(x); + float mu = dot(x, v) / r; + float t = -r * mu - sqrt(r * r * (mu * mu - 1.0) + Rg * Rg); + + vec4 sunTransmittance = _sunTransmittance(v, s, r, mu, sunRadius); + vec3 attenuation; + vec3 inscattering = _getInscatterColor(x, t, v, s, r, mu, attenuation); + + gl_FragColor = _toneMappingUncharted(sunTransmittance + vec4(inscattering, 0.0), 2.0); +} diff --git a/src/render/opengl/shaders/skybox.vert b/src/render/opengl/shaders/skybox.vert new file mode 100644 index 0000000..ff142a5 --- /dev/null +++ b/src/render/opengl/shaders/skybox.vert @@ -0,0 +1,10 @@ +attribute highp vec4 vertex; +uniform highp mat4 viewMatrix; +uniform vec3 cameraLocation; +varying vec3 unprojected; + +void main(void) +{ + unprojected = cameraLocation + vertex.xyz * 500.0; + gl_Position = viewMatrix * vec4(unprojected, 1.0); +} diff --git a/src/render/software/AtmosphereModelBruneton.cpp b/src/render/software/AtmosphereModelBruneton.cpp index 9eb28c9..a723a4d 100644 --- a/src/render/software/AtmosphereModelBruneton.cpp +++ b/src/render/software/AtmosphereModelBruneton.cpp @@ -1268,3 +1268,18 @@ void AtmosphereModelBruneton::fillLightingStatus(LightStatus *status, const Vect status->pushComponent(irradiance); } + +Texture2D *AtmosphereModelBruneton::getTextureTransmittance() const +{ + return _transmittanceTexture; +} + +Texture2D *AtmosphereModelBruneton::getTextureIrradiance() const +{ + return _irradianceTexture; +} + +Texture4D *AtmosphereModelBruneton::getTextureInscatter() const +{ + return _inscatterTexture; +} diff --git a/src/render/software/AtmosphereModelBruneton.h b/src/render/software/AtmosphereModelBruneton.h index 71d768c..7d0aa8b 100644 --- a/src/render/software/AtmosphereModelBruneton.h +++ b/src/render/software/AtmosphereModelBruneton.h @@ -17,6 +17,11 @@ public: AtmosphereResult applyAerialPerspective(Vector3 location, const Color &base); void fillLightingStatus(LightStatus *status, const Vector3 &normal, int opaque); + /* Functions to get access to internal textures (for opengl shaders) */ + Texture2D* getTextureTransmittance() const; + Texture2D* getTextureIrradiance() const; + Texture4D* getTextureInscatter() const; + private: SoftwareRenderer* parent; }; diff --git a/src/render/software/AtmosphereRenderer.h b/src/render/software/AtmosphereRenderer.h index fe05468..bdda48f 100644 --- a/src/render/software/AtmosphereRenderer.h +++ b/src/render/software/AtmosphereRenderer.h @@ -39,6 +39,8 @@ public: virtual AtmosphereResult applyAerialPerspective(Vector3 location, Color base) override; virtual AtmosphereResult getSkyColor(Vector3 direction) override; + inline const AtmosphereModelBruneton* getModel() const {return model;} + private: AtmosphereModelBruneton* model; };