/* 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 . */ #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_selection(view_, 4), fbo_ds (view_, 5, true , GL_RGBA16F), fbo_out (view_, 3, false, GL_RGBA16F), fbo_hsmall (view_, 1, false, GL_RGB16F ), rend_mat(this), rend_service(this) { quad = Primitive::plane(2., 2.); sel_frame = quad->clone(); cam_light = new Light(); cam_light->intensity = 0.75; cam_light->setName("Camera_Light"); line_thick_ = 2.; id_hover = 0; 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[srService] = "service.glsl"; shader_files[srGeometryPass] = "ds_geom.glsl"; shader_files[srLightingPass] = "ds_light.glsl"; shader_files[srFinalPass ] = "ds_final.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);*/ exposure_ = 1.; edit_mode = need_init_shaders = is_camera_light = true; proc_sel_pbo = false; } Renderer::~Renderer() { delete quad; delete sel_frame; delete cam_light; qDeleteAll(shaders.values()); } void Renderer::init(int width, int height) { resize(width, height); rend_mat.init(width, height); rend_service.init(width, height); initQuad(quad); initTextureArrays(); need_init_shaders = true; } void Renderer::resize(int width, int height) { rend_mat.resize(width, height); rend_service.resize(width, height); fbo_selection.enablePixelBuffer(); fbo_selection.resize(width, height); fbo_ds .resize(width, height); fbo_out .resize(width, height); fbo_hsmall .resize(width / 16, height / 16); line_thick_ = lineThickness() + 1.; } void Renderer::reloadShaders() { QMapIterator it(shader_files); while (it.hasNext()) { it.next(); loadShadersMulti(shaders[it.key()], "shaders/" + it.value()); } need_init_shaders = true; } 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; } void Renderer::initShaders() { if (!need_init_shaders) return; need_init_shaders = false; initUniformBuffer(shaders.value(srGeometryPass), &buffer_materials , bpMaterials , "QGLMaterialData" ); initUniformBuffer(shaders.value(srLightingPass), &buffer_materials , bpMaterials , "QGLMaterialData" ); initUniformBuffer(shaders.value(srLightingPass), &buffer_lights , bpLightParameters, "QGLLightParameterData"); initUniformBuffer(shaders.value(srLightingPass), &buffer_lights_pos, bpLightPositions , "QGLLightPositionData" ); } void Renderer::releaseShader() { view->glUseProgram(0); } void Renderer::generateObjectsID(Scene & scene) { ids.clear(); QMapIterator > it(scene.geometries_used); while (it.hasNext()) { it.next(); foreach (ObjectBase * o, it.value()) { uint id = qHash(o); ids[id] = o; o->id_ = id; } } } void Renderer::fillSelectionsBuffer(const QList & ol) { cur_selections_.resize(ol.size()); for (int i = 0; i < ol.size(); ++i) { cur_selections_[i] = (ol[i]->isSelected(true) ? 1 : 0); } } void Renderer::fillObjectsBuffer(const QList & 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); while (it.hasNext()) { it.next(); Mesh * mesh = it.key(); if (mesh->objects_changed) { mesh->objects_changed = false; fillObjectsBuffer(it.value(), pass); mesh->loadObjects(f, cur_objects_); } if (mesh->selected_changed && edit_mode) { mesh->selected_changed = false; fillSelectionsBuffer(it.value()); mesh->loadSelections(f, cur_selections_); } mesh->draw(f, it.value().size()); } } void Renderer::renderSelection(Scene & scene) { QOpenGLShaderProgram * prog = 0; if (bindShader(srSelectionFill, &prog)) { view->hov_objects.clear(); id_hover = 0; if (fbo_selection.queriedPoints() > 0) { if (fbo_selection.queriedPoints() == 1) { id_hover = fbo_selection.getPoint(); view->hov_objects.resize(1); view->hov_objects[0] = ids.value(id_hover); //qDebug() << id_hover; } else { QVector points = fbo_selection.getPoints(); QSet ids_hover; foreach (uint i, points) ids_hover << i; view->hov_objects.clear(); foreach (uint i, ids_hover) view->hov_objects << ids.value(i); //qDebug() << ids_hover; } } fbo_selection.bind(); fbo_selection.setWriteBuffers(); glEnableDepth(); glClearFramebuffer(QColor(0,0,0,0)); setUniformCamera(prog, view->camera()); renderObjects(scene, rpSelection); //mouse_rect = fbo_selection.rect(); if (mouse_rect.isNull()) fbo_selection.queryPoint(0, mouse_pos); else fbo_selection.queryPoints(0, mouse_rect); //qDebug() << id_hover; fbo_selection.bindColorTextures(); fbo_selection.setWriteBuffers(); if (!view->hoverHalo_ && !view->selectionHalo_) glClearFramebuffer(QColor(0,0,0,0), false); else { bindShader(srSelectionHalo, &prog); setUniformHalo(prog, "hover" , view->hoverHaloColor() , view->hoverHaloFillAlpha()); setUniformHalo(prog, "selection", view->selectionHaloColor(), view->selectionHaloFillAlpha()); prog->setUniformValue("has_hover" , view->hoverHalo_ && (id_hover > 0) ? 1.f : 0.f); prog->setUniformValue("has_selection", view->selectionHalo_ ? 1.f : 0.f); prog->setUniformValue("fb_hover" , (int)sbrSrcHover); prog->setUniformValue("fb_selection", (int)sbrSrcSelect); prog->setUniformValue("hover_id", QVector4D(float( id_hover & 0xFF) / 255.f, float((id_hover >> 8 ) & 0xFF) / 255.f, float((id_hover >> 16) & 0xFF) / 255.f, float((id_hover >> 24) & 0xFF) / 255.f)); renderQuad(prog, quad, view->camera()); } fbo_selection.release(); } } void Renderer::renderSelectionFrame() { QOpenGLShaderProgram * prog = 0; if (bindShader(srSelectionFrame, &prog)) { QMatrix4x4 mat; double mrx = mouse_rect.x(), mrw = mouse_rect.width() , vw = view->width(); double mry = mouse_rect.y(), mrh = mouse_rect.height(), vh = view->height(); mat.translate(-1. + (mrw + mrx*2) / vw, 1. - (mrh + mry*2) / vh, 0.); mat.scale(mrw / vw, mrh / vh, 0.); initQuad(sel_frame, mat); prog->setUniformValue("size", QVector2D(mrw / vw, mrh / vh)); prog->setUniformValue("thickness", line_thick_); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); renderQuad(prog, sel_frame); glDisable(GL_BLEND); } } 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; generateObjectsID(scene); reloadMaterials(scene); if (edit_mode) recreateMaterialThumbnails(); emit view->materialsChanged(); } /// material thumbnails if (edit_mode && !scene_changed) { rend_mat.procQueue(); } /// lights QList ll = scene.lights_used; if (is_camera_light) { ll << cam_light; cam_light->setPos(cam->pos()); } if (scene.lights_changed) { scene.lights_changed = false; reloadLightsParameters(ll); } reloadLightsPositions(ll, cam); /// selection if (edit_mode) { 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 pass fbo_ds.bindColorTextures(); fbo_ds.bindDepthTexture(5); fbo_out.bind(); if (bindShader(srLightingPass, &prog)) { setUniformCamera(prog, cam); setUniformViewCorners(prog, cam); for (int i = 0; i < 5; ++i) prog->setUniformValue(QString("tex_%1").arg(i).toLatin1().constData(), i); prog->setUniformValue("tex_d", 5); prog->setUniformValue("lights_count", ll.size()); glClearFramebuffer(view->backColor(), false); renderQuad(prog, quad, cam); /*QVector _fb = fbo_out.grabF(0); if (!_fb.isEmpty()) { double sum = 0.; foreach (float f, _fb) sum += f; sum /= _fb.size(); qDebug() << "sum =" << sum; }*/ } fbo_out.release(); /// apply hovers and selection frame if (edit_mode) { if (bindShader(srSelectionApply, &prog)) { fbo_selection.bindColorTextures(); fbo_out.bindColorTexture(0); prog->setUniformValue("fb_out" , 0); prog->setUniformValue("fb_hover" , (int)sbrHovered ); prog->setUniformValue("fb_select", (int)sbrSelected); renderQuad(prog, quad, cam); if (!mouse_rect.isNull()) { renderSelectionFrame(); } rend_service.renderService(); } } else { fbo_out.blit(0, 0, 0, fbo_out.rect(), QRect(QPoint(), view->size())); } } void Renderer::setCameraLightOn(bool on) { is_camera_light = on; view->scene()->setLightsChanged(); }