276 lines
9.6 KiB
C++
276 lines
9.6 KiB
C++
#include "terrain.h"
|
|
|
|
Terrain::Terrain():
|
|
m_showNormals(Config::terrainShowNormals()), m_skyAnimation(Config::skyAnimation()),
|
|
m_skybox(2048), m_shadowMap(2048),
|
|
m_camera(Config::cameraPosition(), Config::cameraRotation()),
|
|
m_light(Config::skyTime(), Config::skyInclination())
|
|
{
|
|
connect(&m_light, SIGNAL(directionChanged(QVector3D)), &m_shadowMap, SLOT(setLightDirection(QVector3D)));
|
|
|
|
createTerrain();
|
|
m_timeSinceStart.start();
|
|
m_lastFrameTime = m_timeSinceStart.elapsed();
|
|
m_light.updateSunlightDirection();
|
|
}
|
|
|
|
Terrain::~Terrain()
|
|
{
|
|
deleteFrameBufferObject();
|
|
|
|
delete m_landProgram;
|
|
delete m_waterProgram;
|
|
delete m_skyboxProgram;
|
|
delete m_normalProgram;
|
|
delete m_shadingProgram;
|
|
}
|
|
|
|
Light* Terrain::light()
|
|
{
|
|
return &m_light;
|
|
}
|
|
|
|
Camera* Terrain::camera()
|
|
{
|
|
return &m_camera;
|
|
}
|
|
|
|
void Terrain::setSkyAnimation(bool newState)
|
|
{
|
|
m_skyAnimation = newState;
|
|
}
|
|
|
|
void Terrain::setShowNormals(bool newState)
|
|
{
|
|
m_showNormals = newState;
|
|
}
|
|
|
|
QOpenGLShaderProgram* Terrain::getProgram(QString shaderName, bool useGeom)
|
|
{
|
|
QOpenGLShaderProgram* program = new QOpenGLShaderProgram;
|
|
program->addShaderFromSourceFile(QOpenGLShader::Vertex, "shaders/" + shaderName + ".vert");
|
|
if (useGeom) {
|
|
program->addShaderFromSourceFile(QOpenGLShader::Geometry, "shaders/" + shaderName +".geom");
|
|
}
|
|
program->addShaderFromSourceFile(QOpenGLShader::Fragment, "shaders/" + shaderName + ".frag");
|
|
program->link();
|
|
return program;
|
|
}
|
|
|
|
void Terrain::createTerrain()
|
|
{
|
|
QTime time;
|
|
time.start();
|
|
qDebug() << "Generating terrain...";
|
|
|
|
for (int j = -3; j < 4; j ++) {
|
|
for (int i = -3; i < 4; i++) {
|
|
m_blocks.push_back(std::make_shared<Block>(i, j, 32, .5));
|
|
}
|
|
}
|
|
|
|
qDebug() << "Terrain generated in " << double(time.elapsed()) / 1000 << "s";
|
|
}
|
|
|
|
void Terrain::setViewPort(int width, int height)
|
|
{
|
|
m_viewWidth = width;
|
|
m_viewHeight = height;
|
|
m_camera.setRatio(float(width) / float(height));
|
|
deleteFrameBufferObject();
|
|
createFrameBufferObject();
|
|
}
|
|
|
|
void Terrain::createSimpleTextures(GLuint* texturesId, GLsizei nbTextures, GLsizei width, GLsizei height)
|
|
{
|
|
glGenTextures(nbTextures, texturesId);
|
|
|
|
for (GLsizei i = 0; i < nbTextures; i++) {
|
|
glBindTexture(GL_TEXTURE_2D, texturesId[i]);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, width, height, 0, GL_RGB, GL_FLOAT, nullptr);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
|
|
}
|
|
}
|
|
|
|
void Terrain::createFrameBufferObject()
|
|
{
|
|
createSimpleTextures(m_renderedTextures, Terrain::TextureCount - 1, m_viewWidth, m_viewHeight);
|
|
|
|
glGenFramebuffers(2, m_frameBuffers);
|
|
|
|
// G-Buffer
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffers[0]);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_renderedTextures[Terrain::Colors], 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, m_renderedTextures[Terrain::Position], 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, m_renderedTextures[Terrain::Normals], 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, GL_TEXTURE_2D, m_renderedTextures[Terrain::MeshProperties], 0);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, m_renderedTextures[Terrain::WaterDepth], 0);
|
|
|
|
glGenTextures(1, &m_renderedTextures[Terrain::Depth]);
|
|
glBindTexture(GL_TEXTURE_2D, m_renderedTextures[Terrain::Depth]);
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, m_viewWidth, m_viewHeight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
|
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_renderedTextures[Terrain::Depth], 0);
|
|
|
|
GLenum drawGBuffer[5] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4};
|
|
glDrawBuffers(5, drawGBuffer);
|
|
|
|
// Shading buffer
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffers[1]);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
}
|
|
|
|
void Terrain::deleteFrameBufferObject()
|
|
{
|
|
glDeleteFramebuffers(2, m_frameBuffers);
|
|
glDeleteTextures(Terrain::TextureCount, m_renderedTextures);
|
|
}
|
|
|
|
void Terrain::initGl()
|
|
{
|
|
initializeOpenGLFunctions();
|
|
|
|
m_landProgram = new Shader("land", false);
|
|
m_waterProgram = new Shader("water", false);
|
|
m_normalProgram = new Shader("normal", true);
|
|
m_skyboxProgram = new Shader("skybox", false);
|
|
m_shadingProgram = new Shader("shading", false);
|
|
|
|
|
|
for (std::shared_ptr<Block> block: m_blocks) {
|
|
block->water()->initGl(m_waterProgram, m_normalProgram);
|
|
block->land()->initGl(m_landProgram, m_normalProgram);
|
|
}
|
|
|
|
m_skybox.initGl(m_skyboxProgram);
|
|
m_quad.initGl(m_shadingProgram);
|
|
m_shadowMap.initGl();
|
|
m_light.initGl();
|
|
m_camera.initGl();
|
|
|
|
GLint viewPort[4];
|
|
glGetIntegerv(GL_VIEWPORT, viewPort);
|
|
m_viewWidth = viewPort[2];
|
|
m_viewHeight = viewPort[3];
|
|
createFrameBufferObject();
|
|
}
|
|
|
|
void Terrain::setUniforms(Shader* program, RenderPassType renderPass)
|
|
{
|
|
glUniform1i(program->uniformLocation("u_time"), m_timeSinceStart.elapsed());
|
|
glUniform1i(program->uniformLocation("u_renderPass"), renderPass);
|
|
glUniform2i(program->uniformLocation("u_screenSize"), m_viewWidth, m_viewHeight);
|
|
}
|
|
|
|
void Terrain::setAllUniformsForProgram(Shader* program, RenderPassType renderPass)
|
|
{
|
|
program->bind();
|
|
setUniforms(program, renderPass);
|
|
m_light.setUniforms(program, renderPass);
|
|
m_camera.setUniforms(program, renderPass);
|
|
m_shadowMap.setUniforms(program, renderPass);
|
|
program->release();
|
|
}
|
|
|
|
void Terrain::defferedShadingPipeline(RenderPassType renderPass)
|
|
{
|
|
setAllUniformsForProgram(m_landProgram, renderPass);
|
|
setAllUniformsForProgram(m_waterProgram, renderPass);
|
|
setAllUniformsForProgram(m_shadingProgram, renderPass);
|
|
setAllUniformsForProgram(m_skyboxProgram, renderPass);
|
|
|
|
m_shadingProgram->setTexture("colorTexture", GL_TEXTURE0, m_renderedTextures[Terrain::Colors]);
|
|
m_shadingProgram->setTexture("positionTexture", GL_TEXTURE1, m_renderedTextures[Terrain::Position]);
|
|
m_shadingProgram->setTexture("normalTexture", GL_TEXTURE2, m_renderedTextures[Terrain::Normals]);
|
|
m_shadingProgram->setTexture("meshPropertyTexture", GL_TEXTURE4, m_renderedTextures[Terrain::MeshProperties]);
|
|
m_shadingProgram->setTexture("depthTexture", GL_TEXTURE5, m_renderedTextures[Terrain::Depth]);
|
|
m_shadingProgram->setTexture("shadowMapTexture", GL_TEXTURE6, m_shadowMap.depthTexture());
|
|
|
|
|
|
// Geometry pass
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffers[0]);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
|
|
if (m_showNormals) {
|
|
setAllUniformsForProgram(m_normalProgram, RenderPassType::FINAL);
|
|
}
|
|
|
|
for (std::shared_ptr<Block> block: m_blocks) {
|
|
block->land()->render(m_model);
|
|
|
|
if (m_showNormals) {
|
|
block->land()->renderNormals(m_model);
|
|
}
|
|
}
|
|
|
|
// Final pass
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffers[1]);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_renderedTextures[Terrain::Depth], 0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
m_quad.render();
|
|
m_skybox.render();
|
|
}
|
|
|
|
void Terrain::render(GLuint defaultFrameBuffer)
|
|
{
|
|
if (m_skyAnimation) {
|
|
m_light.addToTimeOfDay(float(m_timeSinceStart.elapsed() - m_lastFrameTime) / (120 * 1000) * 1440);
|
|
}
|
|
m_lastFrameTime = m_timeSinceStart.elapsed();
|
|
|
|
// Shadow pass
|
|
m_shadowMap.preRender();
|
|
|
|
setAllUniformsForProgram(m_landProgram, RenderPassType::SHADOW);
|
|
|
|
for (std::shared_ptr<Block> block: m_blocks) {
|
|
block->land()->render(m_model);
|
|
}
|
|
|
|
glViewport(0, 0, m_viewWidth, m_viewHeight);
|
|
|
|
// DEFFERED SHADING PIPELINE
|
|
|
|
// Reflection render
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffers[1]);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_renderedTextures[Terrain::Reflection], 0);
|
|
defferedShadingPipeline(RenderPassType::REFLECTION);
|
|
|
|
// Final render
|
|
glBindFramebuffer(GL_FRAMEBUFFER, m_frameBuffers[1]);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_renderedTextures[Terrain::Refraction], 0);
|
|
defferedShadingPipeline(RenderPassType::FINAL);
|
|
|
|
|
|
// FORWARD SHADING PIPELINE
|
|
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_frameBuffers[1]);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, defaultFrameBuffer);
|
|
glBlitFramebuffer(0, 0, m_viewWidth, m_viewHeight, 0, 0, m_viewWidth, m_viewHeight, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
|
|
|
|
m_waterProgram->setTexture("reflectionTexture", GL_TEXTURE0, m_renderedTextures[Terrain::Reflection]);
|
|
m_waterProgram->setTexture("refractionTexture", GL_TEXTURE1, m_renderedTextures[Terrain::Refraction]);
|
|
m_waterProgram->setTexture("waterDepthTexture", GL_TEXTURE2, m_renderedTextures[Terrain::WaterDepth]);
|
|
m_waterProgram->setTexture("shadowMapTexture", GL_TEXTURE3, m_shadowMap.depthTexture());
|
|
|
|
for (std::shared_ptr<Block> block: m_blocks) {
|
|
block->water()->render(m_model);
|
|
}
|
|
}
|