Files
qad/qglengine/renderer.cpp

331 lines
10 KiB
C++

/*
QGLView
Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define GL_GLEXT_PROTOTYPES
#include <QOpenGLExtraFunctions>
#include "renderer.h"
#include "qglview.h"
#include "glmesh.h"
#include "gltexture_manager.h"
#include <qad_types.h>
using namespace QGLEngineShaders;
Renderer::Renderer(QGLView * view_): RendererBase(view_),
fbo_ds (view_, QVector<GLenum>() << GL_RGBA16F << GL_RGBA32F << GL_RGBA16F << GL_RGBA16F << GL_RGBA16F),
fbo_out (view_, 6, false, GL_RGBA16F),
rend_mat(this), rend_service(this), rend_selection(this), tone_proc(this) {
quad = Primitive::plane(2., 2.);
cam_light = new Light();
cam_light->intensity = 0.75;
cam_light->setName("Camera_Light");
shader_files[srSelectionFill ] = "selection.glsl";
shader_files[srSelectionHalo ] = "selection_halo.glsl";
shader_files[srSelectionApply] = "selection_apply.glsl";
shader_files[srSelectionFrame] = "selection_frame.glsl";
shader_files[srServiceFill ] = "service_fill.glsl";
shader_files[srServiceFrame] = "service_frame.glsl";
shader_files[srServiceLine ] = "service_line.glsl";
shader_files[srGeometryPass ] = "ds_geom.glsl";
shader_files[srLightOmniPass] = "ds_light.glsl";
shader_files[srLightSpotPass] = "ds_light.glsl"; shader_defines[srLightSpotPass] << "SPOT";
shader_files[srFinalPass ] = "ds_final.glsl";
shader_files[srTonemapPass ] = "ds_tonemap.glsl";
/*shaders << ShaderPair("FXAA", &shader_fxaa)
<< ShaderPair("dsl_pass_0", &shader_ds_0)
<< ShaderPair("dsl_pass_1", &shader_ds_1)
<< ShaderPair("hdr", &shader_hdr)
<< ShaderPair("downscale", &shader_small)
<< ShaderPair("bloom_pass_0", &shader_bloom_0)
<< ShaderPair("bloom_pass_1", &shader_bloom_1)
<< ShaderPair("fbo_add", &shader_fbo_add)
<< ShaderPair("motion_blur", &shader_motion_blur)
<< ShaderPair("shadow", &shader_shadow)
<< ShaderPair("ssr", &shader_ssr)
<< ShaderPair("ssr_blur", &shader_ssr_blur)
<< ShaderPair("ssr_merge", &shader_ssr_merge)
<< ShaderPair("ssao_blur", &shader_ssao_blur)
<< ShaderPair("ssao_merge", &shader_ssao_merge)
<< ShaderPair("dof", &shader_dof);*/
shader_fxaa = 0;
exposure_ = 1.;
edit_mode = need_init_shaders = is_camera_light = true;
}
Renderer::~Renderer() {
delete quad;
delete cam_light;
qDeleteAll(shaders.values());
if (shader_fxaa) delete shader_fxaa;
}
void Renderer::init(int width, int height) {
resize(width, height);
rend_mat.init(width, height);
rend_service.init(width, height);
rend_selection.init(width, height);
tone_proc.init();
initQuad(quad);
initTextureArrays();
need_init_shaders = true;
}
void Renderer::resize(int width, int height) {
rend_mat.resize(width, height);
rend_service.resize(width, height);
rend_selection.resize(width, height);
fbo_ds .resize(width, height);
fbo_out .resize(width, height);
tone_proc.resize();
}
void Renderer::reloadShaders() {
QMapIterator<ShaderRole, QString> it(shader_files);
QString dir = "shaders/";
while (it.hasNext()) {
it.next();
loadShadersMulti(shaders[it.key()], dir + it.value(), true, shader_defines.value(it.key()));
}
loadShadersMulti(tone_proc.shader_sum, dir + "sum.glsl", false);
QStringList fxaa_defs;
fxaa_defs << "FXAA_PC 1" << "FXAA_GLSL_130 1" << "FXAA_QUALITY__PRESET 20";
loadShaders(shader_fxaa, QStringList() << (dir + "fxaa.vert") << (dir + "fxaa.frag"), true, fxaa_defs);
need_init_shaders = true;
view->scene()->setLightsChanged();
view->scene()->setTreeStructChanged();
view->scene()->setMaterialsChanged();
}
bool Renderer::bindShader(Renderer::ShaderRole role, QOpenGLShaderProgram ** ret) {
QOpenGLShaderProgram * prog = shaders.value(role);
if (ret) *ret = prog;
if (!prog) return false;
if (!prog->isLinked()) return false;
prog->bind();
return true;
}
bool Renderer::bindShader(QOpenGLShaderProgram * sp) {
if (!sp) return true;
if (!sp->isLinked()) return true;
if (!sp->bind()) return false;
return true;
}
void Renderer::initShaders() {
if (!need_init_shaders) return;
need_init_shaders = false;
initUniformBuffer(shaders.value(srGeometryPass ), &buffer_materials , bpMaterials , "QGLMaterialData" );
initUniformBuffer(shaders.value(srLightOmniPass), &buffer_materials , bpMaterials , "QGLMaterialData" );
initUniformBuffer(shaders.value(srLightOmniPass), &buffer_lights , bpLightParameters, "QGLLightParameterData");
initUniformBuffer(shaders.value(srLightOmniPass), &buffer_lights_pos, bpLightPositions , "QGLLightPositionData" );
initUniformBuffer(shaders.value(srLightSpotPass), &buffer_materials , bpMaterials , "QGLMaterialData" );
initUniformBuffer(shaders.value(srLightSpotPass), &buffer_lights , bpLightParameters, "QGLLightParameterData");
initUniformBuffer(shaders.value(srLightSpotPass), &buffer_lights_pos, bpLightPositions , "QGLLightPositionData" );
ShaderRole roles[] = {srLightOmniPass, srLightSpotPass};
QOpenGLShaderProgram * prog = 0;
for (int p = 0; p < 2; ++p) {
if (!bindShader(roles[p], &prog)) continue;
for (int i = 0; i < 5; ++i)
prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i);
}
if (bindShader(srFinalPass, &prog)) {
for (int i = 0; i < 5; ++i)
prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i);
}
if (bindShader(srTonemapPass, &prog)) {
prog->setUniformValue("tex_0", 0);
prog->setUniformValue("tex_sum", 1);
}
}
void Renderer::releaseShader() {
view->glUseProgram(0);
}
void Renderer::fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass) {
cur_objects_.resize(ol.size());
for (int i = 0; i < ol.size(); ++i) {
Object & so(cur_objects_[i]);
ObjectBase * o = ol[i];
if (o->material()) {
so.material = o->material()->_index;
so.color = QVector4D(1,1,1,1);
} else {
so.material = 0;
so.color = QColor2QVector(o->color_);
}
so.object_id = o->id_;
o->worldTransform().transposed().copyDataTo(so.modelmatrix);
//qDebug() << "load obj" << o->name() << o->worldTransform();
}
//qDebug() << "fillObjectsBuffer" << ol.size();
}
void Renderer::renderObjects(Scene & scene, RenderPass pass) {
QOpenGLExtraFunctions * f = view;
QMapIterator<Mesh*, ObjectBaseList> it(scene.geometries_used[pass]);
bool emit_pos_change = false;
while (it.hasNext()) {
it.next();
Mesh * mesh = it.key();
if (mesh->isObjectsChanged(pass)) {
mesh->setObjectsChanged(pass, false);
emit_pos_change = true;
fillObjectsBuffer(it.value(), pass);
//qDebug() << "loadObjects" << pass << cur_objects_.size();
mesh->loadObjects(f, cur_objects_, pass);
}
if (mesh->isSelectionChanged(pass) && edit_mode) {
mesh->setSelectionChanged(pass, false);
fillSelectionsBuffer(rend_selection.cur_selections_, it.value());
//qDebug() << "fillSelectionsBuffer" << pass << rend_selection.cur_selections_.size();
mesh->loadSelections(f, rend_selection.cur_selections_, pass);
}
//qDebug() << "draw" << pass << it.value().size();
mesh->draw(f, it.value().size(), pass);
}
if (emit_pos_change) emit view->objectsPositionChanged();
}
void Renderer::renderScene() {
initShaders();
QOpenGLExtraFunctions * f = view;
Scene & scene(*(view->scene_));
Camera * cam = view->camera();
QOpenGLShaderProgram * prog = 0;
bool scene_changed = scene.prepare();
/// reload materials on change
if (scene_changed || scene.need_reload_materials) {
//if (scene.need_reload_materials)
// maps_hash = 0;
rend_selection.generateObjectsID(scene);
reloadMaterials(scene);
if (edit_mode)
recreateMaterialThumbnails();
emit view->materialsChanged();
}
/// material thumbnails
if (edit_mode && !scene_changed) {
rend_mat.procQueue();
}
/// lights
QMap<int, QList<Light*>> ll = scene.lights_used;
if (is_camera_light) {
ll[Light::Omni] << cam_light;
cam_light->setPos(cam->pos());
}
if (scene.lights_changed) {
scene.lights_changed = false;
reloadLightsParameters(ll);
}
reloadLightsPositions(cam);
/// selection
if (edit_mode) {
rend_selection.renderSelection(scene);
}
/// geometry pass
fbo_ds.bind();
glEnableDepth();
glClearFramebuffer();
if (bindShader(srGeometryPass, &prog)) {
setUniformMaps(prog);
setUniformCamera(prog, cam);
textures_empty.bind(f, tarEmpty);
textures_maps .bind(f, tarMaps );
renderObjects(scene, rpSolid);
}
fbo_ds.release();
/// lighting passes
fbo_ds.bindColorTextures();
fbo_ds.bindDepthTexture(5);
fbo_out.bind();
//int ri = 1, wi = 0;
typedef QPair<Renderer::ShaderRole, Light::Type> PassPair;
QVector<PassPair> passes;
passes << PassPair(srLightOmniPass, Light::Omni) << PassPair(srLightSpotPass, Light::Cone);
foreach (PassPair pass, passes) {
if (bindShader(pass.first, &prog)) {
setUniformCamera(prog, cam);
setUniformViewCorners(prog, cam);
prog->setUniformValue("lights_start", lights_start[pass.second]);
prog->setUniformValue("lights_count", ll[pass.second].size());
fbo_out.setWriteBuffer(obrSolidOmni + pass.second);
glClearFramebuffer(Qt::black, false);
renderQuad(prog, quad, cam);
}
}
if (bindShader(srFinalPass, &prog)) {
fbo_out.bindColorTexture(obrSolidOmni, 0);
fbo_out.bindColorTexture(obrSolidSpot, 1);
fbo_out.setWriteBuffer(obrSum);
renderQuad(prog, quad);
}
if (tone_proc.process())
fbo_out.bind();
if (bindShader(srTonemapPass, &prog)) {
prog->setUniformValue("exposure", exposure_);
prog->setUniformValue("frame_max", tone_proc.frameMax());
//fbo_1x1.bindColorTexture(0, 1);
fbo_out.bindColorTexture(obrSum, 0);
fbo_out.setWriteBuffer(obrTonemap);
renderQuad(prog, quad);
} else
fbo_out.blit(obrSum, fbo_out.id(), obrTonemap, fbo_out.rect(), fbo_out.rect());
fbo_out.release();
/// apply hovers and selection frame
if (edit_mode) {
rend_selection.drawSelection(fbo_out, obrTonemap);
rend_service.renderService();
} else {
fbo_out.blit(obrTonemap, 0, 0, fbo_out.rect(), QRect(QPoint(), view->size()));
}
//fbo_small_16.blit(0, 0, 0, fbo_small_16.rect(), fbo_small_16.rect(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
}
void Renderer::setCameraLightOn(bool on) {
is_camera_light = on;
view->scene()->setLightsChanged();
}