From 159026571bd069be76a9c9b3bcef46bb286ae0aa Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 2 Jun 2009 09:24:03 +0000 Subject: [PATCH] Added shadow mapping support to Panda --- panda/src/display/graphicsStateGuardian.cxx | 8 + panda/src/display/graphicsStateGuardian.h | 3 + .../glstuff/glGraphicsStateGuardian_src.cxx | 10 +- panda/src/pgraph/renderState.cxx | 34 +- panda/src/pgraph/renderState.h | 13 +- panda/src/pgraph/shaderGeneratorBase.cxx | 29 -- panda/src/pgraph/shaderGeneratorBase.h | 5 - panda/src/pgraphnodes/config_pgraphnodes.cxx | 2 - panda/src/pgraphnodes/shaderGenerator.cxx | 310 ++++++++++++++---- panda/src/pgraphnodes/shaderGenerator.h | 55 ++-- 10 files changed, 313 insertions(+), 156 deletions(-) diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 4453a558a6..2cb936caff 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -46,6 +46,7 @@ #include "depthWriteAttrib.h" #include "lightAttrib.h" #include "texGenAttrib.h" +#include "shaderGenerator.h" #include #include @@ -231,6 +232,8 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, _gamma = 1.0f; _texture_quality_override = Texture::QL_default; + + _shader_generator = NULL; } //////////////////////////////////////////////////////////////////// @@ -246,6 +249,11 @@ GraphicsStateGuardian:: delete _stencil_render_states; _stencil_render_states = 0; } + + if (_shader_generator) { + delete _shader_generator; + _shader_generator = 0; + } GeomMunger::unregister_mungers_for_gsg(this); } diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index a7a2de9652..ca70ec4798 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -50,6 +50,7 @@ class DrawableRegion; class GraphicsEngine; +class ShaderGenerator; //////////////////////////////////////////////////////////////////// // Class : GraphicsStateGuardian @@ -483,6 +484,8 @@ protected: float _gamma; Texture::QualityLevel _texture_quality_override; + + ShaderGenerator* _shader_generator; #ifndef NDEBUG PT(Texture) _flash_texture; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index e937160afd..c65fb386ba 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -60,6 +60,7 @@ #include "lightAttrib.h" #include "scissorAttrib.h" #include "graphicsEngine.h" +#include "shaderGenerator.h" #ifdef HAVE_CG #include "Cg/cgGL.h" @@ -6275,7 +6276,14 @@ set_state_and_transform(const RenderState *target, _target_shader = DCAST(ShaderAttrib, _target_rs->get_attrib_def(ShaderAttrib::get_class_slot())); if (_target_shader->auto_shader()) { - _target_shader = _target_rs->get_generated_shader(); + // If we don't have a generated shader, make sure we have a ShaderGenerator, then generate the shader. + if (_target_rs->_generated_shader == NULL) { + if (_shader_generator == NULL) { + _shader_generator = new ShaderGenerator(this, _scene_setup->get_display_region()->get_window()); + } + const_cast(_target_rs.p())->_generated_shader = DCAST(ShaderAttrib, _shader_generator->synthesize_shader(_target_rs)); + } + _target_shader = DCAST(ShaderAttrib, _target_rs->_generated_shader); } int alpha_test_slot = AlphaTestAttrib::get_class_slot(); diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index f5a5a532fc..f806f356ca 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -1366,29 +1366,6 @@ get_geom_rendering(int geom_rendering) const { return geom_rendering; } -//////////////////////////////////////////////////////////////////// -// Function: RenderState::get_generated_shader -// Access: Public -// Description: Generate a ShaderAttrib for this RenderState. This -// generated ShaderAttrib can be thought of as a -// replacement for the regular ShaderAttrib that is a -// standard part of the RenderState. -//////////////////////////////////////////////////////////////////// -const ShaderAttrib *RenderState:: -get_generated_shader() const { - // This method cannot be declared inline, because of the circular - // dependency on shaderAttrib.h. - - if (_generated_shader != (RenderAttrib*)NULL) { - return DCAST(ShaderAttrib, _generated_shader); - } - ShaderGeneratorBase *gen = ShaderGeneratorBase::get_default(); - ((RenderState*)this)->_generated_shader = - gen->synthesize_shader(this); - return DCAST(ShaderAttrib, _generated_shader); -} - - //////////////////////////////////////////////////////////////////// // Function: RenderState::bin_removed // Access: Public, Static @@ -1598,7 +1575,16 @@ do_compose(const RenderState *other) const { mask.clear_bit(slot); slot = mask.get_lowest_on_bit(); } - + + // If we have any ShaderAttrib with auto-shader enabled, + // remove any shader inputs on it. This is a workaround for an + // issue that makes the shader-generator regenerate the shader + // every time a shader input changes. + CPT(ShaderAttrib) sattrib = DCAST(ShaderAttrib, new_state->get_attrib_def(ShaderAttrib::get_class_slot())); + if (sattrib->auto_shader()) { + sattrib = DCAST(ShaderAttrib, sattrib->clear_all_shader_inputs()); + } + return return_new(new_state); } diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index ed4bdcb2cf..ee9236f51d 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -153,7 +153,6 @@ PUBLISHED: INLINE int get_draw_order() const; INLINE int get_bin_index() const; int get_geom_rendering(int geom_rendering) const; - const ShaderAttrib *get_generated_shader() const; public: static void bin_removed(int bin_index); @@ -206,6 +205,12 @@ private: public: static void init_states(); + // If this state contains an "auto" ShaderAttrib, then an explicit + // ShaderAttrib will be synthesized by the runtime and stored here. + // I can't declare this as a ShaderAttrib because that would create + // a circular include-file dependency problem. Aaargh. + CPT(RenderAttrib) _generated_shader; + private: // This mutex protects _states. It also protects any modification // to the cache, which is encoded in _composition_cache and @@ -291,12 +296,6 @@ private: int _bin_index; int _draw_order; - // If this state contains an "auto" ShaderAttrib, then an explicit - // ShaderAttrib will be synthesized by the runtime and stored here. - // I can't declare this as a ShaderAttrib because that would create - // a circular include-file dependency problem. Aaargh. - CPT(RenderAttrib) _generated_shader; - enum Flags { F_checked_bin_index = 0x000001, F_checked_cull_callback = 0x000002, diff --git a/panda/src/pgraph/shaderGeneratorBase.cxx b/panda/src/pgraph/shaderGeneratorBase.cxx index 7bb1562380..87266caa73 100644 --- a/panda/src/pgraph/shaderGeneratorBase.cxx +++ b/panda/src/pgraph/shaderGeneratorBase.cxx @@ -15,7 +15,6 @@ #include "shaderGeneratorBase.h" TypeHandle ShaderGeneratorBase::_type_handle; -ShaderGeneratorBase *ShaderGeneratorBase::_default_generator = NULL; //////////////////////////////////////////////////////////////////// // Function: ShaderGeneratorBase::Constructor @@ -34,31 +33,3 @@ ShaderGeneratorBase() { ShaderGeneratorBase:: ~ShaderGeneratorBase() { } - -//////////////////////////////////////////////////////////////////// -// Function: ShaderGeneratorBase::get_default -// Access: Published, Static -// Description: Get a pointer to the default shader generator. -//////////////////////////////////////////////////////////////////// -ShaderGeneratorBase *ShaderGeneratorBase:: -get_default() { - return _default_generator; -} - -//////////////////////////////////////////////////////////////////// -// Function: ShaderGeneratorBase::set_default -// Access: Published, Static -// Description: Set the default shader generator. -//////////////////////////////////////////////////////////////////// -void ShaderGeneratorBase:: -set_default(ShaderGeneratorBase *generator) { - if (generator != _default_generator) { - if (_default_generator != (ShaderGeneratorBase *)NULL) { - unref_delete(_default_generator); - } - _default_generator = generator; - if (_default_generator != (ShaderGeneratorBase *)NULL) { - _default_generator->ref(); - } - } -} diff --git a/panda/src/pgraph/shaderGeneratorBase.h b/panda/src/pgraph/shaderGeneratorBase.h index 08ba493635..db54812d55 100644 --- a/panda/src/pgraph/shaderGeneratorBase.h +++ b/panda/src/pgraph/shaderGeneratorBase.h @@ -23,16 +23,11 @@ // Description : This is the abstract base class for ShaderGenerator. //////////////////////////////////////////////////////////////////// class EXPCL_PANDA_PGRAPH ShaderGeneratorBase : public TypedWritableReferenceCount { -private: - static ShaderGeneratorBase *_default_generator; - protected: ShaderGeneratorBase(); PUBLISHED: virtual ~ShaderGeneratorBase(); - static ShaderGeneratorBase *get_default(); - static void set_default(ShaderGeneratorBase *generator); virtual CPT(RenderAttrib) synthesize_shader(const RenderState *rs)=0; public: diff --git a/panda/src/pgraphnodes/config_pgraphnodes.cxx b/panda/src/pgraphnodes/config_pgraphnodes.cxx index f5fac243cd..8a1c18af7d 100644 --- a/panda/src/pgraphnodes/config_pgraphnodes.cxx +++ b/panda/src/pgraphnodes/config_pgraphnodes.cxx @@ -79,6 +79,4 @@ init_libpgraphnodes() { SequenceNode::register_with_read_factory(); Spotlight::register_with_read_factory(); SwitchNode::register_with_read_factory(); - - ShaderGenerator::set_default(new ShaderGenerator()); } diff --git a/panda/src/pgraphnodes/shaderGenerator.cxx b/panda/src/pgraphnodes/shaderGenerator.cxx index 962e6b9378..434d0dc3a3 100644 --- a/panda/src/pgraphnodes/shaderGenerator.cxx +++ b/panda/src/pgraphnodes/shaderGenerator.cxx @@ -33,6 +33,9 @@ #include "directionalLight.h" #include "pointLight.h" #include "spotlight.h" +#include "lightLensNode.h" +#include "graphicsEngine.h" +#include "lvector4.h" TypeHandle ShaderGenerator::_type_handle; @@ -41,9 +44,12 @@ TypeHandle ShaderGenerator::_type_handle; // Access: Published // Description: Create a ShaderGenerator. This has no state, // except possibly to cache certain results. +// The parameter that must be passed is the GSG to +// which the shader generator belongs. //////////////////////////////////////////////////////////////////// ShaderGenerator:: -ShaderGenerator() { +ShaderGenerator(PT(GraphicsStateGuardian) gsg, PT(GraphicsOutput) host) : + _gsg (gsg), _host (host) { } //////////////////////////////////////////////////////////////////// @@ -73,7 +79,7 @@ reset_register_allocator() { //////////////////////////////////////////////////////////////////// // Function: ShaderGenerator::alloc_vreg // Access: Protected -// Description: Allocate a vreg. +// Description: Allocate a vreg. //////////////////////////////////////////////////////////////////// INLINE char *ShaderGenerator:: alloc_vreg() { @@ -135,7 +141,7 @@ analyze_renderstate(const RenderState *rs) { int outputs = aux_bitplane->get_outputs(); // Decide whether or not we need alpha testing or alpha blending. - + const AlphaTestAttrib *alpha_test = DCAST(AlphaTestAttrib, rs->get_attrib_def(AlphaTestAttrib::get_class_slot())); if ((alpha_test->get_mode() != RenderAttrib::M_none)&& (alpha_test->get_mode() != RenderAttrib::M_always)) { @@ -150,9 +156,9 @@ analyze_renderstate(const RenderState *rs) { (transparency->get_mode() == TransparencyAttrib::M_dual)) { _have_alpha_blend = true; } - + // Decide what to send to the framebuffer alpha, if anything. - + if (outputs & AuxBitplaneAttrib::ABO_glow) { if (_have_alpha_blend) { _calc_primary_alpha = true; @@ -171,18 +177,18 @@ analyze_renderstate(const RenderState *rs) { _calc_primary_alpha = true; } } - + // Determine what to put into the aux bitplane. _out_aux_normal = (outputs & AuxBitplaneAttrib::ABO_aux_normal) ? true:false; _out_aux_glow = (outputs & AuxBitplaneAttrib::ABO_aux_glow) ? true:false; _out_aux_any = (_out_aux_normal || _out_aux_glow); - + // Count number of textures. const TextureAttrib *texture = DCAST(TextureAttrib, rs->get_attrib_def(TextureAttrib::get_class_slot())); _num_textures = texture->get_num_on_stages(); - + // Determine whether or not vertex colors or flat colors are present. const ColorAttrib *color = DCAST(ColorAttrib, rs->get_attrib_def(ColorAttrib::get_class_slot())); @@ -194,13 +200,14 @@ analyze_renderstate(const RenderState *rs) { // Break out the lights by type. + _shadows = false; const LightAttrib *la = DCAST(LightAttrib, rs->get_attrib_def(LightAttrib::get_class_slot())); for (int i=0; iget_num_on_lights(); i++) { NodePath light = la->get_on_light(i); nassertv(!light.is_empty()); PandaNode *light_obj = light.node(); nassertv(light_obj != (PandaNode *)NULL); - + if (light_obj->get_type() == AmbientLight::get_class_type()) { _alights_np.push_back(light); _alights.push_back((AmbientLight*)light_obj); @@ -208,6 +215,9 @@ analyze_renderstate(const RenderState *rs) { else if (light_obj->get_type() == DirectionalLight::get_class_type()) { _dlights_np.push_back(light); _dlights.push_back((DirectionalLight*)light_obj); + if (DCAST(LightLensNode, light_obj)->is_shadow_caster()) { + _shadows = true; + } } else if (light_obj->get_type() == PointLight::get_class_type()) { _plights_np.push_back(light); @@ -216,11 +226,14 @@ analyze_renderstate(const RenderState *rs) { else if (light_obj->get_type() == Spotlight::get_class_type()) { _slights_np.push_back(light); _slights.push_back((Spotlight*)light_obj); + if (DCAST(LightLensNode, light_obj)->is_shadow_caster()) { + _shadows = true; + } } } // See if there is a normal map, height map, gloss map, or glow map. - + for (int i=0; i<_num_textures; i++) { TextureStage *stage = texture->get_on_stage(i); TextureStage::Mode mode = stage->get_mode(); @@ -237,25 +250,25 @@ analyze_renderstate(const RenderState *rs) { _map_index_gloss = i; } } - + // Determine whether lighting is needed. if (la->get_num_on_lights() > 0) { _lighting = true; } - + // Find the material. const MaterialAttrib *material = DCAST(MaterialAttrib, rs->get_attrib_def(MaterialAttrib::get_class_slot())); - + if (!material->is_off()) { _material = material->get_material(); } else { _material = Material::get_default(); } - + // Decide which material modes need to be calculated. - + if (_lighting && (_alights.size() > 0)) { if (_material->has_ambient()) { Colorf a = _material->get_ambient(); @@ -277,14 +290,14 @@ analyze_renderstate(const RenderState *rs) { _have_diffuse = true; } } - + if (_lighting && (_material->has_emission())) { Colorf e = _material->get_emission(); if ((e[0]!=0.0)||(e[1]!=0.0)||(e[2]!=0.0)) { _have_emission = true; } } - + if (_lighting && (_dlights.size() + _plights.size() + _slights.size())) { if (_material->has_specular()) { Colorf s = _material->get_specular(); @@ -295,7 +308,7 @@ analyze_renderstate(const RenderState *rs) { _have_specular = true; } } - + // Decide whether to separate ambient and diffuse calculations. if (_have_ambient && _have_diffuse) { @@ -315,14 +328,19 @@ analyze_renderstate(const RenderState *rs) { } const LightRampAttrib *light_ramp = DCAST(LightRampAttrib, rs->get_attrib_def(LightRampAttrib::get_class_slot())); - if (_lighting && + if (_lighting && (light_ramp->get_mode() != LightRampAttrib::LRT_identity)) { _separate_ambient_diffuse = true; } - + + // Do we want to use the ARB_shadow extension? + // This also allows us to use hardware shadows / PCF. + + _use_shadow_filter = _gsg->get_supports_shadow_filter(); + // Does the shader need material properties as input? - - _need_material_props = + + _need_material_props = (_have_ambient && (_material->has_ambient()))|| (_have_diffuse && (_material->has_diffuse()))|| (_have_emission && (_material->has_emission()))|| @@ -349,8 +367,6 @@ analyze_renderstate(const RenderState *rs) { } } - - //////////////////////////////////////////////////////////////////// // Function: ShaderGenerator::clear_analysis // Access: Protected @@ -363,6 +379,7 @@ clear_analysis() { _vertex_colors = false; _flat_colors = false; _lighting = false; + _shadows = false; _have_ambient = false; _have_diffuse = false; _have_emission = false; @@ -378,10 +395,11 @@ clear_analysis() { _subsume_alpha_test = false; _disable_alpha_write = false; _num_clip_planes = 0; + _use_shadow_filter = false; _out_primary_glow = false; - _out_aux_normal = false; - _out_aux_glow = false; - _out_aux_any = false; + _out_aux_normal = false; + _out_aux_glow = false; + _out_aux_any = false; _material = (Material*)NULL; _need_material_props = false; _alights.clear(); @@ -412,22 +430,110 @@ create_shader_attrib(const string &txt) { } for (int i=0; i<(int)_dlights.size(); i++) { shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("dlight", i), _dlights_np[i]); + if (_shadows && _dlights[i]->_shadow_caster) { + PT(Texture) tex = update_shadow_buffer(_dlights_np[i]); + if (tex == NULL) { + pgraph_cat.error() << "Failed to create shadow buffer for DirectionalLight '" << _dlights[i]->get_name() << "'!\n"; + } + shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("dlighttex", i), tex); + } else { + _dlights[i]->clear_shadow_buffers(); + } } for (int i=0; i<(int)_plights.size(); i++) { shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("plight", i), _plights_np[i]); } for (int i=0; i<(int)_slights.size(); i++) { shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("slight", i), _slights_np[i]); + if (_shadows && _slights[i]->_shadow_caster) { + PT(Texture) tex = update_shadow_buffer(_slights_np[i]); + if (tex == NULL) { + pgraph_cat.error() << "Failed to create shadow buffer for Spotlight '" << _slights[i]->get_name() << "'!\n"; + } + shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("slighttex", i), tex); + } else { + _slights[i]->clear_shadow_buffers(); + } } } return shattr; } +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::update_shadow_buffer +// Access: Protected, Virtual +// Description: Updates the depth buffer of the specified light, +// if it is configured to cast shadows. +// Only call this function for DirectionalLights +// and Spotlights. Returns the depth texture. +//////////////////////////////////////////////////////////////////// +PT(Texture) ShaderGenerator:: +update_shadow_buffer(NodePath light_np) { + // Make sure everything is valid. + nassertr(light_np.node()->is_of_type(DirectionalLight::get_class_type()) || + light_np.node()->is_of_type(Spotlight::get_class_type()), NULL); + PT(LightLensNode) light = DCAST(LightLensNode, light_np.node()); + if (light == NULL || !light->_shadow_caster) { + return NULL; + } + + // See if we already have a buffer. If not, create one. + PT(GraphicsOutput) sbuffer; + PT(Texture) tex; + if (light->_sbuffers.count(DCAST(GraphicsStateGuardianBase, _gsg)) == 0) { + + // Nope, the light doesn't have a buffer for our GSG. + FrameBufferProperties fbp; + fbp.set_depth_bits(1); // We only need depth + pgraph_cat.debug() << "Constructing shadow buffer for light '" << light->get_name() + << "', size=" << light->_sb_xsize << "x" << light->_sb_ysize + << ", sort=" << light->_sb_sort << "\n"; + sbuffer = _gsg->get_engine()->make_output(_gsg->get_pipe(), light->get_name(), + light->_sb_sort, fbp, WindowProperties::size(light->_sb_xsize, light->_sb_ysize), + GraphicsPipe::BF_refuse_window, _gsg, _host); + nassertr(sbuffer != NULL, NULL); + + // Create a texture and fill it in with some data to workaround an OpenGL error + tex = new Texture(light->get_name()); + tex->setup_2d_texture(light->_sb_xsize, light->_sb_ysize, Texture::T_float, Texture::F_depth_stencil); + tex->make_ram_image(); + sbuffer->add_render_texture(tex, GraphicsOutput::RTM_bind_or_copy, + DrawableRegion::RTP_depth_stencil); + // Set the wrap mode to BORDER_COLOR + tex->set_wrap_u(Texture::WM_border_color); + tex->set_wrap_v(Texture::WM_border_color); + tex->set_border_color(LVecBase4f(1, 1, 1, 1)); + + if (_use_shadow_filter) { + // If we have the ARB_shadow extension, enable shadow filtering. + tex->set_minfilter(Texture::FT_shadow); + tex->set_magfilter(Texture::FT_shadow); + } else { + // We only accept linear - this tells the GPU to use hardware PCF. + tex->set_minfilter(Texture::FT_linear); + tex->set_magfilter(Texture::FT_linear); + } + sbuffer->make_display_region(0, 1, 0, 1)->set_camera(light_np); + light->_sbuffers[DCAST(GraphicsStateGuardianBase, _gsg)] = DCAST(GraphicsOutputBase, sbuffer); + + } else { + + // There's already a buffer - use that. + sbuffer = DCAST(GraphicsOutput, light->_sbuffers[DCAST(GraphicsStateGuardianBase, _gsg)]); + tex = sbuffer->get_texture(); + nassertr(tex != NULL, NULL); + } + + return tex; +} + //////////////////////////////////////////////////////////////////// // Function: ShaderGenerator::synthesize_shader // Access: Published, Virtual // Description: This is the routine that implements the next-gen // fixed function pipeline by synthesizing a shader. +// It also takes care of setting up any buffers +// needed to produce the requested effects. // // Currently supports: // - flat colors @@ -441,6 +547,7 @@ create_shader_attrib(const string &txt) { // - all texture stage modes, including combine modes // - color scale attrib // - light ramps (for cartoon shading) +// - shadow mapping // // Not yet supported: // - 3D textures, cube textures @@ -458,6 +565,8 @@ synthesize_shader(const RenderState *rs) { analyze_renderstate(rs); reset_register_allocator(); + pgraph_cat.info() << "Generating shader for render state " << rs << "\n"; + // These variables will hold the results of register allocation. char *pos_freg = 0; @@ -470,7 +579,7 @@ synthesize_shader(const RenderState *rs) { pvector texcoord_vreg; pvector texcoord_freg; pvector tslightvec_freg; - + if (_vertex_colors) { _vcregs_used = 1; _fcregs_used = 1; @@ -479,7 +588,7 @@ synthesize_shader(const RenderState *rs) { // Generate the shader's text. ostringstream text; - + text << "//Cg\n"; text << "void vshader(\n"; @@ -516,6 +625,20 @@ synthesize_shader(const RenderState *rs) { text << "\t out float4 l_tangent : " << tangent_freg << ",\n"; text << "\t out float4 l_binormal : " << binormal_freg << ",\n"; } + if (_shadows) { + for (int i=0; i<(int)_dlights.size(); i++) { + if (_dlights[i]->_shadow_caster) { + text << "\t uniform float4x4 trans_model_to_clip_of_dlight" << i << ",\n"; + text << "\t out float4 l_dlightcoord" << i << ",\n"; + } + } + for (int i=0; i<(int)_slights.size(); i++) { + if (_slights[i]->_shadow_caster) { + text << "\t uniform float4x4 trans_model_to_clip_of_slight" << i << ",\n"; + text << "\t out float4 l_slightcoord" << i << ",\n"; + } + } + } } else if (_out_aux_normal) { normal_vreg = alloc_vreg(); normal_freg = alloc_freg(); @@ -523,12 +646,12 @@ synthesize_shader(const RenderState *rs) { text << "\t in float4 vtx_normal : " << normal_vreg << ",\n"; text << "\t out float4 l_normal : " << normal_freg << ",\n"; } - + text << "\t float4 vtx_position : POSITION,\n"; text << "\t out float4 l_position : POSITION,\n"; text << "\t uniform float4x4 mat_modelproj\n"; text << ") {\n"; - + text << "\t l_position = mul(mat_modelproj, vtx_position);\n"; if (_num_clip_planes > 0) { text << "\t l_worldpos = mul(trans_model_to_world, vtx_position);\n"; @@ -552,8 +675,23 @@ synthesize_shader(const RenderState *rs) { text << "\t l_binormal.xyz = mul((float3x3)tpose_view_to_model, -vtx_binormal" << _map_index_normal << ".xyz);\n"; text << "\t l_binormal.w = 0;\n"; } + if (_shadows) { + text << "\t float4x4 biasmat = {0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f};\n"; + for (int i=0; i<(int)_dlights.size(); i++) { + if (_dlights[i]->_shadow_caster) { + text << "\t l_dlightcoord" << i << " = mul(biasmat, mul(trans_model_to_clip_of_dlight" + << i << ", vtx_position + " << _dlights[i]->_push_bias << " * vtx_normal));\n"; + } + } + for (int i=0; i<(int)_slights.size(); i++) { + if (_slights[i]->_shadow_caster) { + text << "\t l_slightcoord" << i << " = mul(biasmat, mul(trans_model_to_clip_of_slight" + << i << ", vtx_position + " << _slights[i]->_push_bias << " * vtx_normal));\n"; + } + } + } text << "}\n\n"; - + text << "void fshader(\n"; if (_num_clip_planes > 0) { text << "\t in float4 l_worldpos,\n"; @@ -571,11 +709,22 @@ synthesize_shader(const RenderState *rs) { } if (_lighting) { text << "\t in float4 l_pos : " << pos_freg << ",\n"; + if (_shadows) { + text << "\t in float4 l_position : " << alloc_freg() << ",\n"; + } for (int i=0; i<(int)_alights.size(); i++) { text << "\t uniform float4 alight_alight" << i << ",\n"; } for (int i=0; i<(int)_dlights.size(); i++) { text << "\t uniform float4x4 dlight_dlight" << i << "_rel_view,\n"; + if (_shadows && _dlights[i]->_shadow_caster) { + if (_use_shadow_filter) { + text << "\t uniform sampler2DShadow k_dlighttex" << i << ",\n"; + } else { + text << "\t uniform sampler2D k_dlighttex" << i << ",\n"; + } + text << "\t float4 l_dlightcoord" << i << ",\n"; + } } for (int i=0; i<(int)_plights.size(); i++) { text << "\t uniform float4x4 plight_plight" << i << "_rel_view,\n"; @@ -583,6 +732,14 @@ synthesize_shader(const RenderState *rs) { for (int i=0; i<(int)_slights.size(); i++) { text << "\t uniform float4x4 slight_slight" << i << "_rel_view,\n"; text << "\t uniform float4 satten_slight" << i << ",\n"; + if (_shadows && _slights[i]->_shadow_caster) { + if (_use_shadow_filter) { + text << "\t uniform sampler2DShadow k_slighttex" << i << ",\n"; + } else { + text << "\t uniform sampler2D k_slighttex" << i << ",\n"; + } + text << "\t float4 l_slightcoord" << i << ",\n"; + } } if (_need_material_props) { text << "\t uniform float4x4 attr_material,\n"; @@ -644,7 +801,10 @@ synthesize_shader(const RenderState *rs) { if (_lighting) { text << "\t // Begin view-space light calculations\n"; text << "\t float ldist,lattenv,langle;\n"; - text << "\t float4 lcolor,lspec,lvec,lpoint,latten,ldir,leye,lhalf;\n"; + text << "\t float4 lcolor,lspec,lvec,lpoint,latten,ldir,leye,lhalf;"; + if (_shadows) { + text << "\t float lshad;\n"; + } if (_separate_ambient_diffuse) { if (_have_ambient) { text << "\t float4 tot_ambient = float4(0,0,0,0);\n"; @@ -680,6 +840,15 @@ synthesize_shader(const RenderState *rs) { text << "\t lspec = dlight_dlight" << i << "_rel_view[1];\n"; text << "\t lvec = dlight_dlight" << i << "_rel_view[2];\n"; text << "\t lcolor *= saturate(dot(l_normal, lvec.xyz));\n"; + if (_shadows && _dlights[i]->_shadow_caster) { + if (_use_shadow_filter) { + text << "\t lshad = shadow2DProj(k_dlighttex" << i << ", l_dlightcoord" << i << ").r;\n"; + } else { + text << "\t lshad = tex2Dproj(k_dlighttex" << i << ", l_dlightcoord" << i << ").r > l_dlightcoord" << i << ".z / l_dlightcoord" << i << ".w;\n"; + } + text << "\t lcolor *= lshad;\n"; + text << "\t lspec *= lshad;\n"; + } if (_have_diffuse) { text << "\t tot_diffuse += lcolor;\n"; } @@ -733,6 +902,16 @@ synthesize_shader(const RenderState *rs) { text << "\t lattenv *= pow(langle, latten.w);\n"; text << "\t if (langle < ldir.w) lattenv = 0;\n"; text << "\t lcolor *= lattenv * saturate(dot(l_normal, lvec.xyz));\n"; + if (_shadows && _slights[i]->_shadow_caster) { + if (_use_shadow_filter) { + text << "\t lshad = shadow2DProj(k_slighttex" << i << ", l_slightcoord" << i << ").r;\n"; + } else { + text << "\t lshad = tex2Dproj(k_slighttex" << i << ", l_slightcoord" << i << ").r > l_slightcoord" << i << ".z / l_slightcoord" << i << ".w;\n"; + } + text << "\t lcolor *= lshad;\n"; + text << "\t lspec *= lshad;\n"; + } + if (_have_diffuse) { text << "\t tot_diffuse += lcolor;\n"; } @@ -837,9 +1016,8 @@ synthesize_shader(const RenderState *rs) { text << "\t result = float4(1,1,1,1);\n"; } } - + text << "\t float4 primary_color = result;\n"; - text << "\t float4 last_saved_result = result;\n"; const TextureAttrib *texture = DCAST(TextureAttrib, rs->get_attrib_def(TextureAttrib::get_class_slot())); bool have_saved_result = false; for (int i=0; i<_num_textures; i++) { @@ -869,14 +1047,14 @@ synthesize_shader(const RenderState *rs) { break; case TextureStage::M_combine: text << "\t result.rgb = "; - text << combine_mode_as_string(stage, stage->get_combine_rgb_mode(), false, i, rs); - text << "\n\t result.a = "; - text << combine_mode_as_string(stage, stage->get_combine_alpha_mode(), true, i, rs); - text << "\n"; + text << combine_mode_as_string(stage, stage->get_combine_rgb_mode(), false, i); + text << ";\n\t result.a = "; + text << combine_mode_as_string(stage, stage->get_combine_alpha_mode(), true, i); + text << ";\n"; break; - case TextureStage::M_blend_color_scale: { + case TextureStage::M_blend_color_scale: text << "\t result.rgb = lerp(result, tex" << i << " * attr_colorscale, tex" << i << ".r).rgb;\n"; - break; } + break; default: break; } @@ -906,7 +1084,7 @@ synthesize_shader(const RenderState *rs) { case RenderAttrib::M_greater_equal: text<<"\t if (result.a < "<= 0) { text << "\t result.a = tex" << _map_index_glow << ".a;\n"; @@ -921,7 +1099,7 @@ synthesize_shader(const RenderState *rs) { text << "\t o_aux.a = 0.5;\n"; } } - + if (_lighting) { if (_have_specular) { if (_material->has_specular()) { @@ -933,7 +1111,7 @@ synthesize_shader(const RenderState *rs) { text << "\t result.rgb = result.rgb + tot_specular.rgb;\n"; } } - + const LightRampAttrib *light_ramp = DCAST(LightRampAttrib, rs->get_attrib_def(LightRampAttrib::get_class_slot())); switch (light_ramp->get_mode()) { case LightRampAttrib::LRT_hdr0: @@ -947,10 +1125,10 @@ synthesize_shader(const RenderState *rs) { break; default: break; } - + // The multiply is a workaround for a radeon driver bug. // It's annoying as heck, since it produces an extra instruction. - text << "\t o_color = result * 1.000001;\n"; + text << "\t o_color = result * 1.000001;\n"; if (_subsume_alpha_test) { text << "\t // Shader subsumes normal alpha test.\n"; } @@ -958,7 +1136,7 @@ synthesize_shader(const RenderState *rs) { text << "\t // Shader disables alpha write.\n"; } text << "}\n"; - + // Insert the shader into the shader attrib. CPT(RenderAttrib) shattr = create_shader_attrib(text.str()); if (_subsume_alpha_test) { @@ -978,38 +1156,35 @@ synthesize_shader(const RenderState *rs) { // Description: This 'synthesizes' a combine mode into a string. //////////////////////////////////////////////////////////////////// const string ShaderGenerator:: -combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode, bool single_value, short texindex, const RenderState *rs) { +combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode, bool single_value, short texindex) { ostringstream text; switch (c_mode) { - case TextureStage::CM_replace: - text << combine_source_as_string(stage, 0, single_value, texindex, rs); - break; case TextureStage::CM_modulate: - text << combine_source_as_string(stage, 0, single_value, texindex, rs); + text << combine_source_as_string(stage, 0, single_value, texindex); text << " * "; - text << combine_source_as_string(stage, 1, single_value, texindex, rs); + text << combine_source_as_string(stage, 1, single_value, texindex); break; case TextureStage::CM_add: - text << combine_source_as_string(stage, 0, single_value, texindex, rs); + text << combine_source_as_string(stage, 0, single_value, texindex); text << " + "; - text << combine_source_as_string(stage, 1, single_value, texindex, rs); + text << combine_source_as_string(stage, 1, single_value, texindex); break; case TextureStage::CM_add_signed: pgraph_cat.error() << "TextureStage::CombineMode ADD_SIGNED not yet supported in per-pixel mode.\n"; break; case TextureStage::CM_interpolate: text << "lerp("; - text << combine_source_as_string(stage, 0, single_value, texindex, rs); + text << combine_source_as_string(stage, 1, single_value, texindex); text << ", "; - text << combine_source_as_string(stage, 1, single_value, texindex, rs); + text << combine_source_as_string(stage, 0, single_value, texindex); text << ", "; - text << combine_source_as_string(stage, 2, true, texindex, rs); + text << combine_source_as_string(stage, 2, true, texindex); text << ")"; break; case TextureStage::CM_subtract: - text << combine_source_as_string(stage, 0, single_value, texindex, rs); + text << combine_source_as_string(stage, 0, single_value, texindex); text << " + "; - text << combine_source_as_string(stage, 1, single_value, texindex, rs); + text << combine_source_as_string(stage, 1, single_value, texindex); break; case TextureStage::CM_dot3_rgb: pgraph_cat.error() << "TextureStage::CombineMode DOT3_RGB not yet supported in per-pixel mode.\n"; @@ -1017,6 +1192,10 @@ combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode case TextureStage::CM_dot3_rgba: pgraph_cat.error() << "TextureStage::CombineMode DOT3_RGBA not yet supported in per-pixel mode.\n"; break; + case TextureStage::CM_replace: + default: // Not sure if this is correct as default value. + text << combine_source_as_string(stage, 0, single_value, texindex); + break; } return text.str(); } @@ -1027,7 +1206,7 @@ combine_mode_as_string(CPT(TextureStage) stage, TextureStage::CombineMode c_mode // Description: This 'synthesizes' a combine source into a string. //////////////////////////////////////////////////////////////////// const string ShaderGenerator:: -combine_source_as_string(CPT(TextureStage) stage, short num, bool single_value, short texindex, const RenderState *rs) { +combine_source_as_string(CPT(TextureStage) stage, short num, bool single_value, short texindex) { TextureStage::CombineSource c_src = TextureStage::CS_undefined; TextureStage::CombineOperand c_op = TextureStage::CO_undefined; switch (num) { @@ -1063,11 +1242,9 @@ combine_source_as_string(CPT(TextureStage) stage, short num, bool single_value, case TextureStage::CS_previous: csource << "result"; break; - case TextureStage::CS_constant_color_scale: { - const ColorScaleAttrib *color_scale = DCAST(ColorScaleAttrib, rs->get_attrib_def(ColorScaleAttrib::get_class_slot())); - LVecBase4f s = color_scale->get_scale(); - csource << "float4(" << s[0] << ", " << s[1] << ", " << s[2] << ", " << s[3] << ")"; - break; } + case TextureStage::CS_constant_color_scale: + csource << "attr_colorscale"; + break; case TextureStage::CS_last_saved_result: csource << "last_saved_result"; break; @@ -1088,3 +1265,4 @@ combine_source_as_string(CPT(TextureStage) stage, short num, bool single_value, } return csource.str(); } + diff --git a/panda/src/pgraphnodes/shaderGenerator.h b/panda/src/pgraphnodes/shaderGenerator.h index 1b61e8f8e8..e71feec9e9 100644 --- a/panda/src/pgraphnodes/shaderGenerator.h +++ b/panda/src/pgraphnodes/shaderGenerator.h @@ -16,6 +16,8 @@ #define SHADERGENERATOR_H #include "pandabase.h" +#include "graphicsStateGuardian.h" +#include "graphicsWindow.h" #include "shaderGeneratorBase.h" #include "nodePath.h" @@ -31,22 +33,23 @@ class ShaderAttrib; // Description : The ShaderGenerator is a device that effectively // replaces the classic fixed function pipeline with // a 'next-gen' fixed function pipeline. The next-gen -// fixed function pipeline supports features like +// fixed function pipeline supports features like // normal mapping, gloss mapping, cartoon lighting, // and so forth. It works by automatically generating // a shader from a given RenderState. // -// Currently, there is a single default ShaderGenerator -// object. It is our intent that in time, people will -// write classes that derive from ShaderGenerator but -// which yield slightly different results. +// Currently, there is one ShaderGenerator object per +// GraphicsStateGuardian. It is our intent that in +// time, people will write classes that derive from +// ShaderGenerator but which yield slightly different +// results. // -// The ShaderGenerator owes its existence to the +// The ShaderGenerator owes its existence to the // 'Bamboo Team' at Carnegie Mellon's Entertainment // Technology Center. This is a group of students // who, as a semester project, decided that next-gen // graphics should be accessible to everyone, even if -// they don't know shader programming. The group +// they don't know shader programming. The group // consisted of: // // Aaron Lo, Programmer @@ -59,18 +62,19 @@ class ShaderAttrib; // //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public ShaderGeneratorBase { +class EXPCL_PANDA_PGRAPHNODES ShaderGenerator : public TypedObject { PUBLISHED: - ShaderGenerator(); + ShaderGenerator(PT(GraphicsStateGuardian) gsg, PT(GraphicsOutput) host); virtual ~ShaderGenerator(); virtual CPT(RenderAttrib) synthesize_shader(const RenderState *rs); - + protected: CPT(RenderAttrib) create_shader_attrib(const string &txt); + PT(Texture) update_shadow_buffer(NodePath light_np); static const string combine_mode_as_string(CPT(TextureStage) stage, - TextureStage::CombineMode c_mode, bool single_value, short texindex, const RenderState *rs); + TextureStage::CombineMode c_mode, bool single_value, short texindex); static const string combine_source_as_string(CPT(TextureStage) stage, - short num, bool single_value, short texindex, const RenderState *rs); + short num, bool single_value, short texindex); // Shader register allocation: @@ -87,7 +91,7 @@ protected: CPT(RenderState) _state; Material *_material; int _num_textures; - + pvector _alights; pvector _dlights; pvector _plights; @@ -96,19 +100,20 @@ protected: pvector _dlights_np; pvector _plights_np; pvector _slights_np; - + bool _vertex_colors; bool _flat_colors; - + bool _lighting; + bool _shadows; bool _have_ambient; bool _have_diffuse; bool _have_emission; bool _have_specular; - + bool _separate_ambient_diffuse; - + int _map_index_normal; int _map_index_height; int _map_index_glow; @@ -124,21 +129,27 @@ protected: bool _calc_primary_alpha; bool _subsume_alpha_test; bool _disable_alpha_write; + int _num_clip_planes; - + bool _use_shadow_filter; + bool _need_material_props; - + void analyze_renderstate(const RenderState *rs); void clear_analysis(); - + + PT(GraphicsStateGuardian) _gsg; + PT(GraphicsOutput) _host; + pmap _generated_shaders; + public: static TypeHandle get_class_type() { return _type_handle; } static void init_type() { - ShaderGeneratorBase::init_type(); + TypedObject::init_type(); register_type(_type_handle, "ShaderGenerator", - ShaderGeneratorBase::get_class_type()); + TypedObject::get_class_type()); } virtual TypeHandle get_type() const { return get_class_type();