From 7755a2e1c7e5bd5511ae927cd1adffd30c8a2d85 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 15 May 2017 14:50:50 +0200 Subject: [PATCH] shadows: provide dummy shadow maps for nonexistent/noncasting lights --- panda/src/display/graphicsStateGuardian.cxx | 54 +++++++++++++++++-- panda/src/display/graphicsStateGuardian.h | 1 + panda/src/glstuff/glCgShaderContext_src.cxx | 3 +- .../glstuff/glGraphicsStateGuardian_src.cxx | 38 +++++++------ .../src/glstuff/glGraphicsStateGuardian_src.h | 2 +- panda/src/glstuff/glShaderContext_src.cxx | 3 +- 6 files changed, 76 insertions(+), 25 deletions(-) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index e8ef03ce3b..294db16e6e 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -1798,6 +1798,13 @@ fetch_specified_texture(Shader::ShaderTexSpec &spec, SamplerState &sampler, } } } + + // There is no such light assigned. Bind a dummy shadow map. + PT(Texture) tex = get_dummy_shadow_map((Texture::TextureType)spec._desired_type); + if (tex != nullptr) { + sampler = tex->get_default_sampler(); + } + return tex; } break; @@ -3152,9 +3159,14 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { node->is_of_type(Spotlight::get_class_type()), NULL); LightLensNode *light = (LightLensNode *)node; - if (light == NULL || !light->_shadow_caster) { - // TODO: return dummy shadow map (all white). - return NULL; + if (light == nullptr || !light->_shadow_caster) { + // This light does not have a shadow caster. Return a dummy shadow map + // that is filled with a depth value of 1. + if (node->is_of_type(PointLight::get_class_type())) { + return get_dummy_shadow_map(Texture::TT_cube_map); + } else { + return get_dummy_shadow_map(Texture::TT_2d_texture); + } } // See if we already have a buffer. If not, create one. @@ -3173,6 +3185,42 @@ get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host) { } } +/** + * Returns a dummy shadow map that can be used for a light of the given type + * that does not cast shadows. + */ +PT(Texture) GraphicsStateGuardian:: +get_dummy_shadow_map(Texture::TextureType texture_type) const { + if (texture_type != Texture::TT_cube_map) { + static PT(Texture) dummy_2d; + if (dummy_2d == nullptr) { + dummy_2d = new Texture("dummy-shadow-2d"); + dummy_2d->setup_2d_texture(1, 1, Texture::T_unsigned_byte, Texture::F_depth_component); + dummy_2d->set_clear_color(1); + if (get_supports_shadow_filter()) { + // If we have the ARB_shadow extension, enable shadow filtering. + dummy_2d->set_minfilter(SamplerState::FT_shadow); + dummy_2d->set_magfilter(SamplerState::FT_shadow); + } else { + dummy_2d->set_minfilter(SamplerState::FT_linear); + dummy_2d->set_magfilter(SamplerState::FT_linear); + } + } + return dummy_2d; + } else { + static PT(Texture) dummy_cube; + if (dummy_cube == nullptr) { + dummy_cube = new Texture("dummy-shadow-cube"); + dummy_cube->setup_cube_map(1, Texture::T_unsigned_byte, Texture::F_depth_component); + dummy_cube->set_clear_color(1); + // Note: cube map shadow filtering doesn't seem to work in Cg. + dummy_cube->set_minfilter(SamplerState::FT_linear); + dummy_cube->set_magfilter(SamplerState::FT_linear); + } + return dummy_cube; + } +} + /** * Creates a depth buffer for shadow mapping. This is a convenience function * for the ShaderGenerator; putting this directly in the ShaderGenerator would diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index a300771f1f..22f4f07fec 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -424,6 +424,7 @@ public: static void create_gamma_table (PN_stdfloat gamma, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table); PT(Texture) get_shadow_map(const NodePath &light_np, GraphicsOutputBase *host=NULL); + PT(Texture) get_dummy_shadow_map(Texture::TextureType texture_type) const; PT(Texture) make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host); #ifdef DO_PSTATS diff --git a/panda/src/glstuff/glCgShaderContext_src.cxx b/panda/src/glstuff/glCgShaderContext_src.cxx index 329594eb38..d8f7231a34 100644 --- a/panda/src/glstuff/glCgShaderContext_src.cxx +++ b/panda/src/glstuff/glCgShaderContext_src.cxx @@ -1078,8 +1078,7 @@ update_shader_texture_bindings(ShaderContext *prev) { if (tex.is_null()) { // Apply a white texture in order to make it easier to use a shader that // takes a texture on a model that doesn't have a texture applied. - _glgsg->set_active_texture_stage(i); - _glgsg->apply_white_texture(); + _glgsg->apply_white_texture(i); continue; } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index e1a250df9a..233385237e 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -10906,25 +10906,20 @@ update_standard_texture_bindings() { /** * Applies a white dummy texture. This is useful to bind to a texture slot - * when a texture is missing. + * when a texture is missing. Also binds the default sampler to the unit. */ void CLP(GraphicsStateGuardian):: -apply_white_texture() { - if (_white_texture != 0) { - glBindTexture(GL_TEXTURE_2D, _white_texture); - return; +apply_white_texture(GLuint unit) { + set_active_texture_stage(unit); + glBindTexture(GL_TEXTURE_2D, get_white_texture()); + + // Also apply the default sampler, if there's a chance we'd applied anything + // else. +#ifndef OPENGLES_1 + if (_supports_sampler_objects) { + _glBindSampler(unit, 0); } - - glGenTextures(1, &_white_texture); - glBindTexture(GL_TEXTURE_2D, _white_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - unsigned char data[] = {0xff, 0xff, 0xff, 0xff}; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, - GL_RGBA, GL_UNSIGNED_BYTE, data); +#endif } /** @@ -10934,7 +10929,16 @@ apply_white_texture() { GLuint CLP(GraphicsStateGuardian):: get_white_texture() { if (_white_texture == 0) { - apply_white_texture(); + glGenTextures(1, &_white_texture); + glBindTexture(GL_TEXTURE_2D, _white_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + unsigned char data[] = {0xff, 0xff, 0xff, 0xff}; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); } return _white_texture; } diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index bf95facb90..5f7f382246 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -567,7 +567,7 @@ protected: void update_shader_vertex_format(const GeomVertexFormat *format); #endif - void apply_white_texture(); + void apply_white_texture(GLuint unit); GLuint get_white_texture(); #ifndef NDEBUG diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index 65a4a1c01c..d81d86bfff 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -2617,8 +2617,7 @@ update_shader_texture_bindings(ShaderContext *prev) { textures[i] = _glgsg->get_white_texture(); samplers[i] = 0; } else { - _glgsg->set_active_texture_stage(i); - _glgsg->apply_white_texture(); + _glgsg->apply_white_texture(i); } continue; }