shadows: provide dummy shadow maps for nonexistent/noncasting lights

This commit is contained in:
rdb 2017-05-15 14:50:50 +02:00
parent 65a705217e
commit 7755a2e1c7
6 changed files with 76 additions and 25 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}