Switched opengl skybox to shaders

This commit is contained in:
Michaël Lemaire 2013-12-21 23:48:54 +01:00
parent c497cf2127
commit 7b790d2015
21 changed files with 948 additions and 207 deletions

View file

@ -20,6 +20,9 @@ namespace basics {
class NoiseGenerator;
class Curve;
class ColorProfile;
class Texture2D;
class Texture3D;
class Texture4D;
}
}
using namespace paysages::basics;

View file

@ -2,6 +2,7 @@
#define DEFINITION_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(DEFINITION_LIBRARY)
# define DEFINITIONSHARED_EXPORT Q_DECL_EXPORT
#else

View file

@ -1,148 +0,0 @@
#include "ExplorerChunkSky.h"
#include <cmath>
#include <GL/gl.h>
#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;
}

View file

@ -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

View file

@ -0,0 +1,109 @@
#include "OpenGLPart.h"
#include <QDir>
#include <cmath>
#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<QString, OpenGLShaderProgram*> 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<QString, OpenGLShaderProgram*> 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<QString, OpenGLShaderProgram*> 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<QString, OpenGLShaderProgram*> 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();
}
}

View file

@ -0,0 +1,46 @@
#ifndef OPENGLPART_H
#define OPENGLPART_H
#include "opengl_global.h"
#include <QMap>
#include <QString>
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<QString, OpenGLShaderProgram*> shaders;
};
}
}
#endif // OPENGLPART_H

View file

@ -1,18 +1,22 @@
#include "OpenGLRenderer.h"
#include <QOpenGLFunctions>
#include <cmath>
#include <GL/gl.h>
#include <GL/glu.h>
#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 &)

View file

@ -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;
};
}

View file

@ -0,0 +1,277 @@
#include "OpenGLShaderProgram.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QDir>
#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<int, unsigned int>(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<int, unsigned int>(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<int, unsigned int>(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<QString, QPair<int, unsigned int> > 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();
}

View file

@ -0,0 +1,61 @@
#ifndef OPENGLSHADERPROGRAM_H
#define OPENGLSHADERPROGRAM_H
#include "opengl_global.h"
#include <QString>
#include <QMatrix4x4>
#include <QColor>
#include <QMap>
#include <QPair>
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<QString, QPair<int, unsigned int> > textures;
};
}
}
#endif // OPENGLSHADERPROGRAM_H

View file

@ -0,0 +1,96 @@
#include "OpenGLSkybox.h"
#include <cmath>
#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;
}

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -16,6 +16,8 @@ namespace opengl {
class WidgetExplorer;
class OpenGLRenderer;
class BaseExplorerChunk;
class OpenGLShaderProgram;
class OpenGLSkybox;
}
}
using namespace paysages::opengl;

View file

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/shaders">
<file>skybox.frag</file>
<file>skybox.vert</file>
</qresource>
</RCC>

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
};

View file

@ -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;
};