From 3c9386de636caafff2a0159ccc1291f5d17eb4ca Mon Sep 17 00:00:00 2001 From: peri4 Date: Thu, 16 Feb 2023 16:57:29 +0300 Subject: [PATCH] first works of omni shadows --- src/core/core/glcubemaparray.cpp | 108 ++++++++++++++++ src/core/core/glcubemaparray.h | 55 +++++++++ src/core/core/glshaders_types.h | 1 + src/core/render/renderer.cpp | 197 ++++++++++++++++++++++-------- src/core/render/renderer.h | 15 ++- src/core/render/renderer_base.cpp | 4 +- src/core/render/renderer_base.h | 2 + 7 files changed, 328 insertions(+), 54 deletions(-) create mode 100644 src/core/core/glcubemaparray.cpp create mode 100644 src/core/core/glcubemaparray.h diff --git a/src/core/core/glcubemaparray.cpp b/src/core/core/glcubemaparray.cpp new file mode 100644 index 0000000..1736091 --- /dev/null +++ b/src/core/core/glcubemaparray.cpp @@ -0,0 +1,108 @@ +/* + QGL CubeMapArray + 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 . +*/ + +#define GL_GLEXT_PROTOTYPES +#include "glcubemaparray.h" + +#include + + +CubeMapArray::CubeMapArray(GLenum format, bool filter) { + target_ = GL_TEXTURE_CUBE_MAP_ARRAY; + format_ = format; + texture_ = 0; + layers_ = 0; + filtering_ = filter; +} + + +CubeMapArray::~CubeMapArray() {} + + +void CubeMapArray::init(QOpenGLExtraFunctions * f) { + if (!isInit()) { + f->glGenTextures(1, &texture_); + } +} + + +void CubeMapArray::destroy(QOpenGLExtraFunctions * f) { + if (texture_ != 0) { + f->glDeleteTextures(1, &texture_); + } + texture_ = 0; +} + + +void CubeMapArray::reinit() { + texture_ = 0; + layers_ = 0; +} + + +void CubeMapArray::bind(QOpenGLExtraFunctions * f, int channel) { + f->glActiveTexture(GL_TEXTURE0 + channel); + f->glBindTexture(target_, texture_); +} + + +void CubeMapArray::release(QOpenGLExtraFunctions * f) { + f->glBindTexture(target_, 0); +} + + +bool CubeMapArray::resize(QOpenGLExtraFunctions * f, QSize new_size, int layers_count) { + if (new_size.isNull() || layers_count <= 0) return false; + if ((size_ == new_size) && (layers_ >= layers_count)) return false; + size_ = new_size; + layers_ = layers_count; + f->glBindTexture(target_, texture_); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_S, GL_REPEAT); + f->glTexParameteri(target_, GL_TEXTURE_WRAP_T, GL_REPEAT); + if (filtering_) { + f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + f->glTexParameteri(target_, GL_TEXTURE_MAX_ANISOTROPY_EXT, 8); + } else { + f->glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + f->glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + GLenum bformat = format_; + GLenum btype = GL_UNSIGNED_BYTE; + if (format_ == GL_R16F || format_ == GL_R32F) { + bformat = GL_RED; + btype = GL_FLOAT; + } + f->glTexImage3D(target_, 0, format_, size_.width(), size_.height(), layers_ * 6, 0, bformat, btype, nullptr); + return true; +} + + +void CubeMapArray::load(QOpenGLExtraFunctions * f, const QImage & image, int layer) { + if (image.isNull() || size_.isNull() || layer < 0 || layer >= layers_) return; + QImage im = + image.mirrored(false, true).scaled(size_, Qt::IgnoreAspectRatio, Qt::SmoothTransformation).convertToFormat(QImage::Format_RGBA8888); + // qDebug() << "CubeMapArray::load image" << image.size() << "to layer" << layer; + // f->glTexSubImage3D(target_, 0, 0, 0, layer, size_.width(), size_.height(), 1, GL_RGBA, GL_UNSIGNED_BYTE, im.constBits()); +} + + +void CubeMapArray::mipmaps(QOpenGLExtraFunctions * f) { + f->glBindTexture(target_, texture_); + f->glGenerateMipmap(target_); +} diff --git a/src/core/core/glcubemaparray.h b/src/core/core/glcubemaparray.h new file mode 100644 index 0000000..392b8b1 --- /dev/null +++ b/src/core/core/glcubemaparray.h @@ -0,0 +1,55 @@ +/* + QGL Texture2DArray + 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 . +*/ + +#ifndef GLCUBEMAPARRAY_H +#define GLCUBEMAPARRAY_H + +#include "gltypes.h" + + +class QGLENGINE_CORE_EXPORT CubeMapArray { +public: + CubeMapArray(GLenum format, bool filter); + ~CubeMapArray(); + + void init(QOpenGLExtraFunctions * f); + void destroy(QOpenGLExtraFunctions * f); + void reinit(); + + void bind(QOpenGLExtraFunctions * f, int channel = 0); + void release(QOpenGLExtraFunctions * f); + + // returns true if size changed + bool resize(QOpenGLExtraFunctions * f, QSize new_size, int layers_count); + void load(QOpenGLExtraFunctions * f, const QImage & image, int layer); + void mipmaps(QOpenGLExtraFunctions * f); + + GLuint ID() const { return texture_; } + bool isInit() const { return texture_ != 0; } + int layersCount() const { return layers_; } + +private: + GLenum target_, format_; + GLuint texture_; + QSize size_; + int layers_; + bool filtering_; +}; + + +#endif // GLCUBEMAPARRAY_H diff --git a/src/core/core/glshaders_types.h b/src/core/core/glshaders_types.h index 1579692..8452c17 100644 --- a/src/core/core/glshaders_types.h +++ b/src/core/core/glshaders_types.h @@ -108,6 +108,7 @@ enum TextureArrayRole { tarEmpty = 10, tarMaps = 11, tarShadowsCone = 12, + tarShadowsOmni = 13, }; enum EmptyMapRole { emrWhite = 0, diff --git a/src/core/render/renderer.cpp b/src/core/render/renderer.cpp index 1d5395a..abc349d 100644 --- a/src/core/render/renderer.cpp +++ b/src/core/render/renderer.cpp @@ -40,6 +40,31 @@ Renderer::Renderer(QGLView * view_) , 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; @@ -61,10 +86,12 @@ Renderer::Renderer(QGLView * view_) shader_files[srFinalPass] = "ds_final.glsl"; shader_files[srTonemapPass] = "ds_tonemap.glsl"; - shader_files[srShadowPass] = "shadow.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; @@ -89,6 +116,7 @@ void Renderer::init(int width, int height) { textures_maps.reinit(); textures_empty.reinit(); shadow_maps_cone.reinit(); + shadow_maps_omni.reinit(); resize(width, height); rend_mat.init(width, height); rend_service.init(width, height); @@ -115,6 +143,7 @@ void Renderer::resize(int width, int height) { void Renderer::reloadShaders() { + __reinit_debug = true; QMapIterator it(shader_files); qDeleteAll(shaders.values()); shaders.clear(); @@ -122,7 +151,7 @@ void Renderer::reloadShaders() { shader_fxaa = nullptr; if (tone_proc.shader_sum) delete tone_proc.shader_sum; tone_proc.shader_sum = nullptr; - QString dir = ":/shaders/"; + QString dir = "./shaders/"; while (it.hasNext()) { it.next(); loadShadersMulti(shaders[it.key()], dir + it.value(), true, shader_defines.value(it.key())); @@ -165,9 +194,6 @@ bool Renderer::bindShader(QOpenGLShaderProgram * sp) { void Renderer::initShaders() { if (!need_init_shaders) return; need_init_shaders = false; - // initUniformBuffer(shaders.value(srGeometrySolidPass), &buffer_materials, bpMaterials, "QGLMaterialData"); - // initUniformBuffer(shaders.value(srGeometryTransparentPass), &buffer_materials, bpMaterials, "QGLMaterialData"); - // initUniformBuffer(shaders.value(srShadowPass), &buffer_materials, bpMaterials, "QGLMaterialData"); QOpenGLShaderProgram * prog = 0; for (ShaderRole role: {srLightOmniPass, srLightSpotPass}) { if (!bindShader(role, &prog)) continue; @@ -188,7 +214,7 @@ void Renderer::initShaders() { prog->setUniformValue("tex_t_0", 3); prog->setUniformValue("tex_t_1", 4); } - for (ShaderRole role: {srGeometrySolidPass, srGeometryTransparentPass, srShadowPass}) { + for (ShaderRole role: {srGeometrySolidPass, srGeometryTransparentPass, srShadowConePass, srShadowOmniPass}) { if (!bindShader(role, &prog)) continue; initUniformBuffer(prog, &buffer_materials, bpMaterials, "QGLMaterialData"); setUniformMaps(prog); @@ -317,16 +343,71 @@ void Renderer::renderLight(int first_wr_buff, bool clear_only) { prog->setUniformValue("view_mat", cam->viewMatrix().inverted().toGenericMatrix<3, 3>()); prog->setUniformValue("shadow_size", view->shadow_map_size); prog->setUniformValue("tex_shadows_cone", (int)tarShadowsCone); + prog->setUniformValue("tex_shadows_omni", (int)tarShadowsOmni); prog->setUniformValue("soft_shadows_enabled", view->soft_shadows); prog->setUniformValue("soft_shadows_samples", view->soft_shadows_samples); shadow_maps_cone.bind(view, tarShadowsCone); + shadow_maps_omni.bind(view, tarShadowsOmni); renderQuad(prog, quad, cam); } } } -void Renderer::renderShadow(int index, Light * light) { +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); + shadow_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); + shadow_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()) { @@ -336,13 +417,7 @@ void Renderer::renderShadow(int index, Light * light) { light->shadow_map.resize(view->shadow_map_size, force_resize); light->shadow_map.bind(); - if (!framebufferTextureLayer) { - framebufferTextureLayer = view->context()->getProcAddress("glFramebufferTextureLayer"); - } - if (framebufferTextureLayer) { - ((PFNGLFRAMEBUFFERTEXTURELAYERPROC)framebufferTextureLayer)(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, shadow_maps_cone.ID(), 0, index); - // glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadow_maps_cone.ID(), 0, index); - } + view->glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, shadow_maps_cone.ID(), 0, index); glDrawBuffer(GL_COLOR_ATTACHMENT0); glClearFramebuffer(); renderObjects(scene, rpSolid); @@ -350,6 +425,36 @@ void Renderer::renderShadow(int index, Light * light) { } +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); + int buff_ind = GL_COLOR_ATTACHMENT0 + i; + view->glFramebufferTextureLayer(GL_FRAMEBUFFER, buff_ind, shadow_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(buff_ind); + glClearFramebuffer(); + renderObjects(scene, rpSolid); + } + light->shadow_map.release(); +} + + void Renderer::renderScene() { timings.clear(); Measurer phase(&timings); @@ -363,6 +468,7 @@ void Renderer::renderScene() { 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(); @@ -383,35 +489,14 @@ void Renderer::renderScene() { } phase.end(); - /// shadows and shadow matrix - phase.begin("shadows"); - if (bindShader(srShadowPass, &prog)) { - glEnableDepth(); - textures_empty.bind(f, tarEmpty); - textures_maps.bind(f, tarMaps); - auto cam_ivm = cam->fullViewMatrix().inverted(); - auto cone_ll = cur_lights.value(Light::Cone); - shadow_maps_cone.resize(f, view->shadow_map_size, cone_ll.size()); - QMatrix4x4 mat_vp; - mat_vp.scale(0.5, 0.5); - mat_vp.translate(1, 1); - 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); - renderShadow(i, l); - } - l->shadow_matrix = mat_vp * vpm * cam_ivm; - } - } + /// 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 @@ -558,13 +643,29 @@ void Renderer::renderScene() { // qDebug() << last_img.size(); } phase.end(); + /* - auto cone_ll = cur_lights.value(Light::Cone); - if (!cone_ll.isEmpty()) { - Light * l = cone_ll[0]; - l->shadow_map.blit(0, 0, 0, l->shadow_map.rect(), l->shadow_map.rect()); + 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); + 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); + quad->draw(f, 1);*/ } diff --git a/src/core/render/renderer.h b/src/core/render/renderer.h index e7d56b2..a55ceda 100644 --- a/src/core/render/renderer.h +++ b/src/core/render/renderer.h @@ -57,7 +57,8 @@ class QGLENGINE_CORE_EXPORT Renderer: public RendererBase { srLightSpotPass, srFinalPass, srTonemapPass, - srShadowPass, + srShadowConePass, + srShadowOmniPass, }; enum OutBufferRole { obrSolidOmni, @@ -112,7 +113,10 @@ protected: void fillObjectsBuffer(const ObjectBaseList & ol, RenderPass pass); void renderObjects(Scene & scene, RenderPass pass); void renderLight(int first_wr_buff, bool clear_only); - void renderShadow(int index, Light * light); + void renderConeShadows(); + void renderOmniShadows(); + void renderConeShadow(int index, Light * light); + void renderOmniShadow(int index, Light * light, QOpenGLShaderProgram * prog, QMatrix4x4 om); bool bindShader(ShaderRole role, QOpenGLShaderProgram ** ret = 0); bool bindShader(QOpenGLShaderProgram * sp); @@ -124,7 +128,7 @@ protected: private: float gamma_ = 1.f; int camera_light_mode, cur_write_plane = 0, prev_write_plane = 0; - bool edit_mode, need_init_shaders, need_render_sum; + bool edit_mode, need_init_shaders, need_render_sum, __reinit_debug; Framebuffer fbo_ds, fbo_out; QMap shader_files; QMap shader_defines; @@ -143,6 +147,8 @@ private: QPoint mouse_pos; QRect mouse_rect; QMatrix4x4 prev_view, prev_proj; + QMatrix4x4 mat_norm_to_tex_coord, mat_camera_fullview_inv, mat_proj_90; + QMatrix4x4 mat_faces[6]; QMatrix3x3 nm; QVector4D corner_dirs[4]; QVector hcontent; @@ -150,8 +156,7 @@ private: QVector fb_effects; QImage last_img; QString timings; - bool is_grabbing = false; - QFunctionPointer framebufferTextureLayer = nullptr; + bool is_grabbing = false; }; #endif // RENDERER_H diff --git a/src/core/render/renderer_base.cpp b/src/core/render/renderer_base.cpp index 40c3c7a..ded4ac2 100644 --- a/src/core/render/renderer_base.cpp +++ b/src/core/render/renderer_base.cpp @@ -37,7 +37,8 @@ RendererBase::RendererBase(QGLView * view_) , buffer_lights_pos(GL_UNIFORM_BUFFER, GL_STREAM_DRAW) , textures_empty(GL_RGBA, false) , textures_maps(GL_RGBA, true) - , shadow_maps_cone(GL_R32F, false) { + , shadow_maps_cone(GL_R32F, false) + , shadow_maps_omni(GL_R32F, false) { textures_manager = new TextureManager(view); maps_size = QSize(1024, 1024); maps_hash = 0; @@ -62,6 +63,7 @@ void RendererBase::initTextureArrays() { im.fill(0xFF8080); textures_empty.load(f, im, emrBlue); shadow_maps_cone.init(f); + shadow_maps_omni.init(f); } diff --git a/src/core/render/renderer_base.h b/src/core/render/renderer_base.h index 421aa87..5411d90 100644 --- a/src/core/render/renderer_base.h +++ b/src/core/render/renderer_base.h @@ -20,6 +20,7 @@ #define RENDERER_BASE_H #include "glbuffer.h" +#include "glcubemaparray.h" #include "glshaders_types.h" #include "gltexturearray.h" #include "measurer.h" @@ -59,6 +60,7 @@ protected: Buffer buffer_materials; Buffer buffer_lights, buffer_lights_pos; Texture2DArray textures_empty, textures_maps, shadow_maps_cone; + CubeMapArray shadow_maps_omni; QSize maps_size; uint maps_hash; GLuint tex_coeff[2];