716 lines
22 KiB
C++
716 lines
22 KiB
C++
/*
|
|
QGL Renderer
|
|
Ivan Pelipenko peri4ko@yandex.ru
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#define GL_GLEXT_PROTOTYPES
|
|
#include "renderer.h"
|
|
|
|
#include "glmesh.h"
|
|
#include "glshaders.h"
|
|
#include "gltexture_manager.h"
|
|
#include "qglview.h"
|
|
|
|
#include <QOpenGLExtraFunctions>
|
|
#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 << GL_RGBA32F << GL_RGBA16F)
|
|
, fbo_out(view_, obrBuffersCount, false, GL_RGBA16F)
|
|
, rend_mat(this)
|
|
, rend_service(this)
|
|
, rend_selection(this)
|
|
, tone_proc(this)
|
|
, tex_env(view_, 512) {
|
|
mat_norm_to_tex_coord.scale(0.5, 0.5);
|
|
mat_norm_to_tex_coord.translate(1, 1);
|
|
mat_proj_90 = glMatrixPerspective(90., 1., 0.1);
|
|
for (int i = 0; i < 6; ++i) {
|
|
QMatrix4x4 fvm;
|
|
switch (i) {
|
|
case 0:
|
|
fvm.rotate(-90., QVector3D(0, 1, 0));
|
|
fvm.rotate(180., QVector3D(0, 0, 1));
|
|
break;
|
|
case 1:
|
|
fvm.rotate(90., QVector3D(0, 1, 0));
|
|
fvm.rotate(180., QVector3D(0, 0, 1));
|
|
break;
|
|
case 2: fvm.rotate(-90., QVector3D(1, 0, 0)); break;
|
|
case 3: fvm.rotate(90., QVector3D(1, 0, 0)); break;
|
|
case 4:
|
|
fvm.rotate(180., QVector3D(0, 0, 1));
|
|
fvm.rotate(180., QVector3D(0, 1, 0));
|
|
break;
|
|
case 5: fvm.rotate(180., QVector3D(0, 0, 1)); break;
|
|
}
|
|
mat_faces[i] = fvm;
|
|
}
|
|
|
|
quad = Primitive::plane(2., 2.);
|
|
cam_light = new Light();
|
|
cam_light->intensity = 0.75;
|
|
cam_light->setName("Camera_Light");
|
|
cam_light->setCastShadows(false);
|
|
|
|
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[srGeometrySolidPass] = "ds_geom.glsl";
|
|
shader_files[srGeometryTransparentPass] = "ds_geom.glsl";
|
|
shader_files[srLightOmniPass] = "ds_light.glsl";
|
|
shader_files[srLightSpotPass] = "ds_light.glsl";
|
|
shader_files[srFinalPass] = "ds_final.glsl";
|
|
shader_files[srTonemapPass] = "ds_tonemap.glsl";
|
|
|
|
shader_files[srShadowConePass] = "shadow.glsl";
|
|
shader_files[srShadowOmniPass] = "shadow.glsl";
|
|
|
|
shader_defines[srGeometrySolidPass] << "SOLID";
|
|
shader_defines[srLightSpotPass] << "SPOT";
|
|
shader_defines[srShadowOmniPass] << "OMNI";
|
|
|
|
edit_mode = need_init_shaders = true;
|
|
camera_light_mode = QGLView::clmAuto;
|
|
}
|
|
|
|
|
|
Renderer::~Renderer() {
|
|
delete quad;
|
|
delete cam_light;
|
|
qDeleteAll(shaders.values());
|
|
if (shader_fxaa) delete shader_fxaa;
|
|
}
|
|
|
|
|
|
void Renderer::init(int width, int height) {
|
|
fbo_ds.reinit();
|
|
fbo_out.reinit();
|
|
quad->reinit();
|
|
buffer_materials.reinit();
|
|
buffer_lights.reinit();
|
|
buffer_lights_pos.reinit();
|
|
textures_maps.reinit();
|
|
textures_empty.reinit();
|
|
shadow_maps_cone.reinit();
|
|
shadow_maps_omni.reinit();
|
|
depth_maps_cone.reinit();
|
|
depth_maps_omni.reinit();
|
|
resize(width, height);
|
|
rend_mat.init(width, height);
|
|
rend_service.init(width, height);
|
|
rend_selection.init(width, height);
|
|
tone_proc.init();
|
|
initQuad(quad);
|
|
initTextureArrays();
|
|
initCoeffTextures();
|
|
markReloadTextures();
|
|
tex_env.init();
|
|
if (is_grabbing) fbo_out.enablePixelBuffer();
|
|
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() {
|
|
__reinit_debug = true;
|
|
QMapIterator<ShaderRole, QString> it(shader_files);
|
|
qDeleteAll(shaders.values());
|
|
shaders.clear();
|
|
if (shader_fxaa) delete shader_fxaa;
|
|
shader_fxaa = nullptr;
|
|
if (tone_proc.shader_sum) delete tone_proc.shader_sum;
|
|
tone_proc.shader_sum = nullptr;
|
|
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 15";
|
|
loadShaders(shader_fxaa, QStringList() << (dir + "fxaa.vert") << (dir + "fxaa.frag"), true, fxaa_defs);
|
|
for (auto * e: fb_effects) {
|
|
e->reloadShaders();
|
|
e->is_loaded = true;
|
|
}
|
|
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;
|
|
QOpenGLShaderProgram * prog = 0;
|
|
for (ShaderRole role: {srLightOmniPass, srLightSpotPass}) {
|
|
if (!bindShader(role, &prog)) continue;
|
|
initUniformBuffer(prog, &buffer_materials, bpMaterials, "QGLMaterialData");
|
|
initUniformBuffer(prog, &buffer_lights, bpLightParameters, "QGLLightParameterData");
|
|
initUniformBuffer(prog, &buffer_lights_pos, bpLightPositions, "QGLLightPositionData");
|
|
for (int i = 0; i < 5; ++i)
|
|
prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i);
|
|
prog->setUniformValue("tex_coeffs[0]", (int)Renderer::dbrBuffersCount);
|
|
prog->setUniformValue("tex_env", (int)Renderer::dbrBuffersCount + 1);
|
|
prog->setUniformValue("tex_noise", (int)Renderer::dbrBuffersCount + 2);
|
|
setUniformMaps(prog);
|
|
}
|
|
if (bindShader(srFinalPass, &prog)) {
|
|
prog->setUniformValue("tex_g1", 0);
|
|
prog->setUniformValue("tex_s_0", 1);
|
|
prog->setUniformValue("tex_s_1", 2);
|
|
prog->setUniformValue("tex_t_0", 3);
|
|
prog->setUniformValue("tex_t_1", 4);
|
|
}
|
|
for (ShaderRole role: {srGeometrySolidPass, srGeometryTransparentPass, srShadowConePass, srShadowOmniPass}) {
|
|
if (!bindShader(role, &prog)) continue;
|
|
initUniformBuffer(prog, &buffer_materials, bpMaterials, "QGLMaterialData");
|
|
setUniformMaps(prog);
|
|
}
|
|
if (bindShader(srTonemapPass, &prog)) {
|
|
prog->setUniformValue("tex_0", 0);
|
|
prog->setUniformValue("tex_sum", 1);
|
|
}
|
|
}
|
|
|
|
|
|
void Renderer::releaseShader() {
|
|
view->glUseProgram(0);
|
|
}
|
|
|
|
|
|
QVector<int> Renderer::getFreePlanes(int count) {
|
|
prev_write_plane = cur_write_plane;
|
|
QVector<int> ret;
|
|
bool output_done = false;
|
|
const int total_count = 4;
|
|
for (int i = 0; i < total_count; ++i) {
|
|
int plane = obrGeneral0 + i;
|
|
if (prev_write_plane == plane) continue;
|
|
if (!output_done) {
|
|
fbo_out.setWriteBuffer(plane);
|
|
cur_write_plane = plane;
|
|
output_done = true;
|
|
continue;
|
|
}
|
|
ret << plane;
|
|
if (ret.size() == count) break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
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();
|
|
so.flags = 0;
|
|
so.f_accept_light = o->isAcceptLight();
|
|
so.f_accept_fog = o->isAcceptFog();
|
|
so.f_accept_cast_shadow = o->isCastShadows();
|
|
so.f_accept_rec_shadow = o->isReceiveShadows();
|
|
o->worldTransform().transposed().copyDataTo(so.modelmatrix);
|
|
o->textureGLMatrix().copyDataTo(so.texturematrix);
|
|
// qDebug() << "load obj" << o->name() << o->textureMatrix() << tmat;
|
|
}
|
|
// 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::renderLight(int first_wr_buff, bool clear_only) {
|
|
QOpenGLShaderProgram * prog = 0;
|
|
Camera * cam = view->camera();
|
|
for (int i = 0; i < 3; ++i) {
|
|
view->glActiveTexture(GL_TEXTURE0 + Renderer::dbrBuffersCount + i);
|
|
view->glBindTexture(GL_TEXTURE_2D, tex_coeff[i]);
|
|
}
|
|
fbo_ds.bindColorTextures();
|
|
fbo_out.bind();
|
|
// tex_env.bind((int)Renderer::dbrBuffersCount + 1);
|
|
typedef QPair<Renderer::ShaderRole, Light::Type> PassPair;
|
|
QVector<PassPair> passes;
|
|
passes << PassPair(srLightOmniPass, Light::Omni) << PassPair(srLightSpotPass, Light::Cone);
|
|
QColor back = view->fogColor();
|
|
back.setAlpha(0);
|
|
foreach(PassPair pass, passes) {
|
|
if (bindShader(pass.first, &prog)) {
|
|
/*if (pass.second == Light::Cone) {
|
|
auto & cone_ll(cur_lights[pass.second]);
|
|
for (int i = 0; i < cone_ll.size(); ++i) {
|
|
Light * l = cone_ll[i];
|
|
// l->shadow_map.bindDepthTexture(mtShadowCone + i);
|
|
l->shadow_map.bindColorTexture(0, mtShadowCone + i);
|
|
}
|
|
}*/
|
|
fbo_out.setWriteBuffer(first_wr_buff + pass.second);
|
|
glClearFramebuffer(back, false);
|
|
if (clear_only) continue;
|
|
setUniformCamera(prog, cam);
|
|
setUniformViewCorners(prog, cam);
|
|
prog->setUniformValue("lights_start", lights_start[pass.second]);
|
|
prog->setUniformValue("lights_count", (int)cur_lights[pass.second].size());
|
|
prog->setUniformValue("fog_color", view->fogColor());
|
|
prog->setUniformValue("fog_decay", qMax(view->fogDecay(), 0.001f));
|
|
prog->setUniformValue("fog_density", view->fogDensity());
|
|
prog->setUniformValue("view_mat", cam->viewMatrix().inverted().toGenericMatrix<3, 3>());
|
|
prog->setUniformValue("shadow_size", view->shadow_map_size);
|
|
prog->setUniformValue("noise_size", noise_size);
|
|
prog->setUniformValue("soft_shadows_enabled", view->soft_shadows);
|
|
prog->setUniformValue("soft_shadows_samples", view->soft_shadows_samples);
|
|
prog->setUniformValue("soft_shadows_quality", view->soft_shadows_quality);
|
|
prog->setUniformValue("tex_shadows_cone", (int)tarShadowsCone);
|
|
prog->setUniformValue("tex_shadows_omni", (int)tarShadowsOmni);
|
|
prog->setUniformValue("tex_depths_cone", (int)tarDepthsCone);
|
|
prog->setUniformValue("tex_depths_omni", (int)tarDepthsOmni);
|
|
GLenum filter = view->softShadows() ? GL_NEAREST : GL_LINEAR;
|
|
shadow_maps_cone.bind(view, tarShadowsCone);
|
|
view->glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, filter);
|
|
view->glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, filter);
|
|
shadow_maps_omni.bind(view, tarShadowsOmni);
|
|
view->glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MIN_FILTER, filter);
|
|
view->glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_MAG_FILTER, filter);
|
|
depth_maps_cone.bind(view, tarDepthsCone);
|
|
depth_maps_omni.bind(view, tarDepthsOmni);
|
|
renderQuad(prog, quad, cam);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Renderer::renderConeShadows() {
|
|
QOpenGLExtraFunctions * f = view;
|
|
QOpenGLShaderProgram * prog = 0;
|
|
if (bindShader(srShadowConePass, &prog)) {
|
|
glEnableDepth();
|
|
textures_empty.bind(f, tarEmpty);
|
|
textures_maps.bind(f, tarMaps);
|
|
auto cone_ll = cur_lights.value(Light::Cone);
|
|
if (shadow_maps_cone.resize(f, view->shadow_map_size, cone_ll.size())) {
|
|
f->glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
f->glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
f->glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
f->glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
|
|
}
|
|
depth_maps_cone.resize(f, view->shadow_map_size, cone_ll.size());
|
|
for (int i = 0; i < cone_ll.size(); ++i) {
|
|
Light * l = cone_ll[i];
|
|
QMatrix4x4 pm = glMatrixPerspective(l->angle_end, 1., 0.1), om, vm;
|
|
om.translate(-l->worldAim());
|
|
vm.translate(0., 0., -l->distance());
|
|
// vm.rotate(-roll_, 0., 0., 1.);
|
|
QMatrix4x4 pmat = l->worldTransform();
|
|
pmat(0, 3) = pmat(1, 3) = pmat(2, 3) = 0.;
|
|
vm *= pmat.inverted();
|
|
auto vpm = pm * (vm * om);
|
|
if (l->isCastShadows()) {
|
|
prog->setUniformValue("qgl_ViewProjMatrix", vpm);
|
|
renderConeShadow(i, l);
|
|
}
|
|
l->shadow_matrix = mat_norm_to_tex_coord * vpm * mat_camera_fullview_inv;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Renderer::renderOmniShadows() {
|
|
QOpenGLExtraFunctions * f = view;
|
|
QOpenGLShaderProgram * prog = 0;
|
|
if (bindShader(srShadowOmniPass, &prog)) {
|
|
glEnableDepth();
|
|
textures_empty.bind(f, tarEmpty);
|
|
textures_maps.bind(f, tarMaps);
|
|
auto omni_ll = cur_lights.value(Light::Omni);
|
|
if (shadow_maps_omni.resize(f, view->shadow_map_size, omni_ll.size())) {
|
|
f->glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
f->glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LESS);
|
|
}
|
|
depth_maps_omni.resize(f, view->shadow_map_size, omni_ll.size());
|
|
for (int i = 0; i < omni_ll.size(); ++i) {
|
|
Light * l = omni_ll[i];
|
|
QMatrix4x4 om;
|
|
om.translate(-l->worldPos());
|
|
// vm.translate(0., 0., -l->distance());
|
|
// vm.rotate(-roll_, 0., 0., 1.);
|
|
if (l->isCastShadows()) {
|
|
renderOmniShadow(i, l, prog, om);
|
|
}
|
|
// l->shadow_matrix = mat_norm_to_tex_coord * vpm * mat_camera_fullview_inv;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Renderer::renderConeShadow(int index, Light * light) {
|
|
Scene & scene(*(view->scene()));
|
|
bool force_resize = false;
|
|
if (!light->shadow_map.isInit()) {
|
|
light->shadow_map.reinit(view);
|
|
force_resize = true;
|
|
}
|
|
light->shadow_map.resize(view->shadow_map_size, force_resize);
|
|
light->shadow_map.bind();
|
|
|
|
view->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadow_maps_cone.ID(), 0, index);
|
|
view->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, depth_maps_cone.ID(), 0, index);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
glClearFramebuffer();
|
|
renderObjects(scene, rpSolid);
|
|
light->shadow_map.release();
|
|
}
|
|
|
|
|
|
void Renderer::renderOmniShadow(int index, Light * light, QOpenGLShaderProgram * prog, QMatrix4x4 om) {
|
|
Scene & scene(*(view->scene()));
|
|
bool force_resize = false;
|
|
if (!light->shadow_map.isInit()) {
|
|
light->shadow_map.reinit(view);
|
|
force_resize = true;
|
|
}
|
|
light->shadow_map.resize(view->shadow_map_size, force_resize);
|
|
light->shadow_map.bind();
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
QMatrix4x4 vm = mat_proj_90 * mat_faces[i] * om;
|
|
prog->setUniformValue("qgl_ViewProjMatrix", vm);
|
|
view->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadow_maps_omni.ID(), 0, index * 6 + i);
|
|
view->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, depth_maps_omni.ID(), 0, index * 6 + i);
|
|
// static GLenum faces[6] = {GL_COLOR_ATTACHMENT0,
|
|
// GL_COLOR_ATTACHMENT1,
|
|
// GL_COLOR_ATTACHMENT2,
|
|
// GL_COLOR_ATTACHMENT3,
|
|
// GL_COLOR_ATTACHMENT4,
|
|
// GL_COLOR_ATTACHMENT5};
|
|
// view->glDrawBuffers(6, faces);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
glClearFramebuffer();
|
|
renderObjects(scene, rpSolid);
|
|
}
|
|
light->shadow_map.release();
|
|
}
|
|
|
|
|
|
void Renderer::renderScene() {
|
|
timings.clear();
|
|
Measurer phase(&timings);
|
|
|
|
phase.begin("init");
|
|
initShaders();
|
|
tex_env.load();
|
|
QOpenGLExtraFunctions * f = view;
|
|
Scene & scene(*(view->scene()));
|
|
Camera * cam = view->camera();
|
|
QOpenGLShaderProgram * prog = 0;
|
|
bool scene_changed = scene.prepare();
|
|
cur_lights = scene.lights_used;
|
|
mat_camera_fullview_inv = cam->fullViewMatrix().inverted();
|
|
scene.destroyUnused(f);
|
|
phase.end();
|
|
|
|
/// reload materials on change
|
|
phase.begin("scene reload");
|
|
if (scene_changed || scene.need_reload_materials) {
|
|
rend_selection.generateObjectsID(scene);
|
|
reloadMaterials(scene);
|
|
if (edit_mode) recreateMaterialThumbnails();
|
|
emit view->materialsChanged();
|
|
}
|
|
phase.end();
|
|
|
|
/// material thumbnails
|
|
phase.begin("materials");
|
|
if (edit_mode && !scene_changed) {
|
|
rend_mat.procQueue();
|
|
}
|
|
phase.end();
|
|
|
|
/// cone shadows and shadow matrix
|
|
phase.begin("shadows cone");
|
|
renderConeShadows();
|
|
phase.end();
|
|
|
|
/// omni shadows and shadow matrix
|
|
phase.begin("shadows omni");
|
|
renderOmniShadows();
|
|
phase.end();
|
|
|
|
/// lights
|
|
phase.begin("lights prepare");
|
|
bool use_camlight = (camera_light_mode == QGLView::clmOn);
|
|
if ((camera_light_mode == QGLView::clmAuto) && cur_lights.isEmpty()) {
|
|
use_camlight = true;
|
|
}
|
|
if (use_camlight) {
|
|
cur_lights[Light::Omni] << cam_light;
|
|
cam_light->setPos(cam->pos());
|
|
}
|
|
if (scene.lights_changed) {
|
|
scene.lights_changed = false;
|
|
reloadLightsParameters(cur_lights);
|
|
}
|
|
reloadLightsPositions(cam);
|
|
phase.end();
|
|
|
|
/// selection
|
|
phase.begin("selection");
|
|
if (edit_mode) {
|
|
rend_selection.renderSelection(scene);
|
|
}
|
|
phase.end();
|
|
|
|
|
|
/// solid geometry pass
|
|
phase.begin("geometry solid");
|
|
fbo_ds.bind();
|
|
glEnableDepth();
|
|
glClearFramebuffer();
|
|
if (bindShader(srGeometrySolidPass, &prog)) {
|
|
setUniformCamera(prog, cam);
|
|
textures_empty.bind(f, tarEmpty);
|
|
textures_maps.bind(f, tarMaps);
|
|
glPolygonMode(GL_FRONT_AND_BACK, view->renderMode());
|
|
renderObjects(scene, rpSolid);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
}
|
|
fbo_ds.blit(dbrNormalZ, fbo_ds.id(), dbrNormalZSolid, fbo_ds.rect(), fbo_ds.rect());
|
|
fbo_ds.blit(dbrMetalRoughReflectFlags, fbo_ds.id(), dbrMetalRoughReflectFlagsSolid, fbo_ds.rect(), fbo_ds.rect());
|
|
fbo_ds.release();
|
|
phase.end();
|
|
|
|
/// lighting passes
|
|
phase.begin("... light");
|
|
renderLight(obrSolidOmni, scene.geometries_used[rpSolid].isEmpty());
|
|
phase.end();
|
|
|
|
/// transparent geometry pass
|
|
phase.begin("geometry trans");
|
|
fbo_ds.bind();
|
|
glEnableDepth();
|
|
fbo_ds.setWriteBuffers({0, 1, 2, 3, 4});
|
|
glClearFramebuffer(Qt::black, false);
|
|
fbo_ds.setWriteBuffers();
|
|
if (bindShader(srGeometryTransparentPass, &prog)) {
|
|
setUniformCamera(prog, cam);
|
|
textures_empty.bind(f, tarEmpty);
|
|
textures_maps.bind(f, tarMaps);
|
|
glPolygonMode(GL_FRONT_AND_BACK, view->renderMode());
|
|
renderObjects(scene, rpTransparent);
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
}
|
|
fbo_ds.release();
|
|
phase.end();
|
|
|
|
/// lighting passes
|
|
phase.begin("... light");
|
|
renderLight(obrTransparentOmni, scene.geometries_used[rpTransparent].isEmpty());
|
|
phase.end();
|
|
|
|
/// blending layers
|
|
phase.begin("blending");
|
|
if (bindShader(srFinalPass, &prog)) {
|
|
fbo_out.bindColorTexture(obrSolidOmni, 1);
|
|
fbo_out.bindColorTexture(obrSolidSpot, 2);
|
|
fbo_out.bindColorTexture(obrTransparentOmni, 3);
|
|
fbo_out.bindColorTexture(obrTransparentSpot, 4);
|
|
fbo_out.setWriteBuffer(obrLighting);
|
|
renderQuad(prog, quad);
|
|
}
|
|
phase.end();
|
|
|
|
fbo_out.bind();
|
|
cur_write_plane = obrLighting;
|
|
|
|
/// tonemapping
|
|
phase.begin("tonemap");
|
|
tone_proc.process();
|
|
auto free = getFreePlanes(0);
|
|
if (bindShader(srTonemapPass, &prog)) {
|
|
prog->setUniformValue("gamma", gamma_);
|
|
prog->setUniformValue("frame_max", tone_proc.frameMax());
|
|
// qDebug() << tone_proc.frameMax();
|
|
fbo_out.bindColorTexture(prev_write_plane, 0);
|
|
renderQuad(prog, quad);
|
|
} else {
|
|
fbo_out.blit(prev_write_plane, fbo_out.id(), cur_write_plane, fbo_out.rect(), fbo_out.rect());
|
|
}
|
|
phase.end();
|
|
|
|
/// FXAA
|
|
phase.begin("fxaa");
|
|
if (view->FXAA_) {
|
|
prog = shader_fxaa;
|
|
if (bindShader(prog)) {
|
|
auto free = getFreePlanes(0);
|
|
setUniformCamera(prog, 0, true, fbo_out.size());
|
|
fbo_out.bindColorTexture(prev_write_plane);
|
|
renderQuad(prog, quad, 0, false);
|
|
}
|
|
}
|
|
phase.end();
|
|
|
|
/// custom effects
|
|
for (auto * e: fb_effects) {
|
|
if (!e->isEnabled()) continue;
|
|
phase.begin("fb effect " + e->name());
|
|
e->reloadShadersInternal();
|
|
e->drawInternal();
|
|
phase.end();
|
|
}
|
|
|
|
fbo_out.release();
|
|
|
|
/// apply hovers and selection frame
|
|
phase.begin("service");
|
|
if (edit_mode) {
|
|
rend_selection.drawSelection(fbo_out, cur_write_plane);
|
|
rend_service.renderService();
|
|
} else {
|
|
fbo_out.blit(cur_write_plane, 0, 0, fbo_out.rect(), QRect(QPoint(), view->pixelSize()));
|
|
}
|
|
phase.end();
|
|
|
|
/// grab framebuffer
|
|
phase.begin("grab");
|
|
if (is_grabbing) {
|
|
fbo_out.queryImage(0);
|
|
last_img = fbo_out.getImage().mirrored();
|
|
// qDebug() << last_img.size();
|
|
}
|
|
phase.end();
|
|
|
|
/*
|
|
static QOpenGLShaderProgram * tprog = nullptr;
|
|
if (tprog && __reinit_debug) {
|
|
delete tprog;
|
|
tprog = nullptr;
|
|
}
|
|
if (!tprog) {
|
|
loadShadersMulti(tprog, "./shaders/debug.glsl");
|
|
tprog->bind();
|
|
tprog->setUniformValue("tex_cone", (int)tarShadowsCone);
|
|
tprog->setUniformValue("tex_omni", (int)tarShadowsOmni);
|
|
tprog->setUniformValue("dep_cone", (int)tarDepthsCone);
|
|
tprog->setUniformValue("dep_omni", (int)tarDepthsOmni);
|
|
QMatrix4x4 mat;
|
|
double sz = 0.33;
|
|
mat.translate(sz - 1, sz - 1);
|
|
mat.scale(sz, sz);
|
|
tprog->setUniformValue("qgl_ViewProjMatrix", mat);
|
|
}
|
|
__reinit_debug = false;
|
|
tprog->bind();
|
|
shadow_maps_cone.bind(f, tarShadowsCone);
|
|
shadow_maps_omni.bind(f, tarShadowsOmni);
|
|
depth_maps_cone.bind(f, tarDepthsCone);
|
|
depth_maps_omni.bind(f, tarDepthsOmni);
|
|
quad->draw(f, 1);
|
|
*/
|
|
}
|
|
|
|
|
|
void Renderer::setCameraLightMode(int m) {
|
|
camera_light_mode = m;
|
|
view->scene()->setLightsChanged();
|
|
}
|
|
|
|
void Renderer::setGrabImage(bool on) {
|
|
is_grabbing = on;
|
|
// fbo_out.enablePixelBuffer();
|
|
}
|
|
|
|
|
|
void Renderer::addFramebufferEffect(FramebufferEffectBase * e) {
|
|
e->r = this;
|
|
fb_effects << e;
|
|
}
|