/* QGLView Copyright (C) 2020 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 . */ #define GL_GLEXT_PROTOTYPES #include #include "renderer.h" #include "qglview.h" #include "glmesh.h" #include "gltexture_manager.h" #include using namespace QGLEngineShaders; Renderer::Renderer(QGLView * view_): RendererBase(view_), fbo_ds (view_, QVector() << GL_RGBA16F << GL_RGBA32F << GL_RGBA16F << GL_RGBA16F << 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) { 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; gamma_ = 1.; 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) { 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(); tex_env.init(); 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 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 15"; 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); prog->setUniformValue("tex_coeffs[0]", (int)Renderer::dbrBuffersCount); //prog->setUniformValue("tex_coeffs[1]", (int)Renderer::dbrBuffersCount+1); prog->setUniformValue("tex_env", (int)Renderer::dbrBuffersCount+1); } 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); } if (bindShader(srGeometryPass, &prog)) { setUniformMaps(prog); } 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 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 < 2; ++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); //int ri = 1, wi = 0; typedef QPair PassPair; QVector 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)) { 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", 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>()); renderQuad(prog, quad, cam); } } } void Renderer::renderScene() { initShaders(); tex_env.load(); QOpenGLExtraFunctions * f = view; Scene & scene(*(view->scene_)); Camera * cam = view->camera(); QOpenGLShaderProgram * prog = 0; bool scene_changed = scene.prepare(); scene.destroyUnused(f); /// 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 cur_lights = scene.lights_used; 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); /// selection if (edit_mode) { rend_selection.renderSelection(scene); } /// solid geometry pass fbo_ds.bind(); glEnableDepth(); glClearFramebuffer(); if (bindShader(srGeometryPass, &prog)) { setUniformCamera(prog, cam); textures_empty.bind(f, tarEmpty); textures_maps .bind(f, tarMaps ); renderObjects(scene, rpSolid); } fbo_ds.release(); /// lighting passes renderLight(obrSolidOmni, scene.geometries_used[rpSolid].isEmpty()); /// transparent geometry pass fbo_ds.bind(); glEnableDepth(); glClearFramebuffer(Qt::black, false); if (bindShader(srGeometryPass, &prog)) { renderObjects(scene, rpTransparent); } fbo_ds.release(); /// lighting passes renderLight(obrTransparentOmni, scene.geometries_used[rpTransparent].isEmpty()); /// blending layers if (bindShader(srFinalPass, &prog)) { //fbo_ds .bindColorTexture(dbrNormalZ , 0); fbo_out.bindColorTexture(obrSolidOmni , 1); fbo_out.bindColorTexture(obrSolidSpot , 2); fbo_out.bindColorTexture(obrTransparentOmni, 3); fbo_out.bindColorTexture(obrTransparentSpot, 4); fbo_out.setWriteBuffer(obrSum); renderQuad(prog, quad); } /// tonemapping if (tone_proc.process()) fbo_out.bind(); if (bindShader(srTonemapPass, &prog)) { prog->setUniformValue("gamma", gamma_); prog->setUniformValue("frame_max", tone_proc.frameMax()); 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::setCameraLightMode(int m) { camera_light_mode = m; view->scene()->setLightsChanged(); }