From 1d446824636f29b362e9760a613a4a7bc119d38a Mon Sep 17 00:00:00 2001 From: Josh Yelon Date: Fri, 25 Jan 2008 03:35:12 +0000 Subject: [PATCH] Much progress implementing shader generator --- panda/src/pgraph/renderState.I | 1 + panda/src/pgraph/renderState.cxx | 4 +- panda/src/pgraph/renderState.h | 1 - panda/src/pgraph/shaderGenerator.I | 17 - panda/src/pgraph/shaderGenerator.cxx | 579 +++++++++++++++++++++++++-- panda/src/pgraph/shaderGenerator.h | 85 +++- 6 files changed, 635 insertions(+), 52 deletions(-) diff --git a/panda/src/pgraph/renderState.I b/panda/src/pgraph/renderState.I index af4e190e5d..f9dda73518 100644 --- a/panda/src/pgraph/renderState.I +++ b/panda/src/pgraph/renderState.I @@ -651,3 +651,4 @@ CompositionCycleDescEntry(const RenderState *obj, _inverted(inverted) { } + diff --git a/panda/src/pgraph/renderState.cxx b/panda/src/pgraph/renderState.cxx index 503c32f195..34caf1259e 100644 --- a/panda/src/pgraph/renderState.cxx +++ b/panda/src/pgraph/renderState.cxx @@ -39,6 +39,7 @@ #include "mutexHolder.h" #include "thread.h" #include "attribSlots.h" +#include "shaderGenerator.h" ReMutex *RenderState::_states_lock = NULL; RenderState::States *RenderState::_states = NULL; @@ -1142,8 +1143,9 @@ get_generated_shader() const { if (_generated_shader != (RenderAttrib*)NULL) { return DCAST(ShaderAttrib, _generated_shader); } + ShaderGenerator *gen = ShaderGenerator::get_default(); ((RenderState*)this)->_generated_shader = - ShaderGenerator::synthesize_shader(this); + gen->synthesize_shader(this); return DCAST(ShaderAttrib, _generated_shader); } diff --git a/panda/src/pgraph/renderState.h b/panda/src/pgraph/renderState.h index d87e6f86e4..815823738f 100644 --- a/panda/src/pgraph/renderState.h +++ b/panda/src/pgraph/renderState.h @@ -36,7 +36,6 @@ #include "deletedChain.h" #include "simpleHashMap.h" #include "cacheStats.h" -#include "shaderGenerator.h" class GraphicsStateGuardianBase; class FogAttrib; diff --git a/panda/src/pgraph/shaderGenerator.I b/panda/src/pgraph/shaderGenerator.I index 3e40c20856..5c7787c4ac 100644 --- a/panda/src/pgraph/shaderGenerator.I +++ b/panda/src/pgraph/shaderGenerator.I @@ -16,20 +16,3 @@ // //////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////// -// Function: ShaderGenerator::Constructor -// Access: Private -// Description: This class is never constructed. -//////////////////////////////////////////////////////////////////// -ShaderGenerator:: -ShaderGenerator() { -} - -//////////////////////////////////////////////////////////////////// -// Function: ShaderGenerator::Destructor -// Access: Private -// Description: This class is never constructed. -//////////////////////////////////////////////////////////////////// -ShaderGenerator:: -~ShaderGenerator() { -} diff --git a/panda/src/pgraph/shaderGenerator.cxx b/panda/src/pgraph/shaderGenerator.cxx index 19d57051b4..74019e0d7d 100644 --- a/panda/src/pgraph/shaderGenerator.cxx +++ b/panda/src/pgraph/shaderGenerator.cxx @@ -15,50 +15,407 @@ // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// +// +// Lighting calculations. These can be done either in tangent space +// or model space. Doing them in tangent space is necessary for +// normal mapping. However, it is only possible if tangents have +// been computed. If not, then model space is the only option. +// The parameters needed for lighting are: +// +// Model Space: +// +// vshader: +// model-space normal - varying +// model-space vertex position - varying +// fshader: +// model-space normal - varying +// model-space vertex position - varying +// model-space light position (spot or point) - uniform +// model-space light vector (directional) - uniform +// model-space eye position - uniform +// +// Tangent Space: +// +// vshader: +// model-space normal - varying +// model-space tangent - varying +// model-space binormal - varying +// model-space light position (spot or point) - uniform +// model-space light vector (directional) - uniform +// model-space eye position - uniform +// fshader: +// model-space vertex position - varying +// tangent-space light vector, one per light - varying +// tangent-space eye vector - varying +// model-space light position (spot) - uniform +// +///////////////////////////////////////////////////////////////////// + #include "renderState.h" #include "shaderAttrib.h" #include "shaderGenerator.h" +#include "ambientLight.h" +#include "directionalLight.h" +#include "pointLight.h" +#include "spotLight.h" + +TypeHandle ShaderGenerator::_type_handle; +PT(ShaderGenerator) ShaderGenerator::_default_generator; + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::Constructor +// Access: Published +// Description: Create a ShaderGenerator. This has no state, +// except possibly to cache certain results. +//////////////////////////////////////////////////////////////////// +ShaderGenerator:: +ShaderGenerator() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::Destructor +// Access: Published, Virtual +// Description: Destroy a ShaderGenerator. +//////////////////////////////////////////////////////////////////// +ShaderGenerator:: +~ShaderGenerator() { +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::reset_register_allocator +// Access: Protected +// Description: Clears the register allocator. Initially, the pool +// of available registers is empty. You have to add +// some if you want there to be any. +//////////////////////////////////////////////////////////////////// +void ShaderGenerator:: +reset_register_allocator() { + _vtregs_used = 0; + _vcregs_used = 0; + _ftregs_used = 0; + _fcregs_used = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::alloc_vreg +// Access: Protected +// Description: Allocate a vreg. +//////////////////////////////////////////////////////////////////// +INLINE char *ShaderGenerator:: +alloc_vreg() { + switch (_vtregs_used) { + case 0: _vtregs_used += 1; return "TEXCOORD0"; + case 1: _vtregs_used += 1; return "TEXCOORD1"; + case 2: _vtregs_used += 1; return "TEXCOORD2"; + case 3: _vtregs_used += 1; return "TEXCOORD3"; + case 4: _vtregs_used += 1; return "TEXCOORD4"; + case 5: _vtregs_used += 1; return "TEXCOORD5"; + case 6: _vtregs_used += 1; return "TEXCOORD6"; + case 7: _vtregs_used += 1; return "TEXCOORD7"; + } + switch (_vcregs_used) { + case 0: _vcregs_used += 1; return "COLOR0"; + case 1: _vcregs_used += 1; return "COLOR1"; + } + return "UNKNOWN"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::alloc_freg +// Access: Protected +// Description: Allocate a freg. +//////////////////////////////////////////////////////////////////// +INLINE char *ShaderGenerator:: +alloc_freg() { + switch (_ftregs_used) { + case 0: _ftregs_used += 1; return "TEXCOORD0"; + case 1: _ftregs_used += 1; return "TEXCOORD1"; + case 2: _ftregs_used += 1; return "TEXCOORD2"; + case 3: _ftregs_used += 1; return "TEXCOORD3"; + case 4: _ftregs_used += 1; return "TEXCOORD4"; + case 5: _ftregs_used += 1; return "TEXCOORD5"; + case 6: _ftregs_used += 1; return "TEXCOORD6"; + case 7: _ftregs_used += 1; return "TEXCOORD7"; + } + switch (_fcregs_used) { + case 0: _fcregs_used += 1; return "COLOR0"; + case 1: _fcregs_used += 1; return "COLOR1"; + } + return "UNKNOWN"; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::get_default +// Access: Published, Static +// Description: Get a pointer to the default shader generator. +//////////////////////////////////////////////////////////////////// +ShaderGenerator *ShaderGenerator:: +get_default() { + if (_default_generator == (ShaderGenerator *)NULL) { + _default_generator = new ShaderGenerator; + } + return _default_generator; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::set_default +// Access: Published, Static +// Description: Set the default shader generator. +//////////////////////////////////////////////////////////////////// +void ShaderGenerator:: +set_default(ShaderGenerator *generator) { + _default_generator = generator; +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::analyze_renderstate +// Access: Protected +// Description: Analyzes the RenderState prior to shader generation. +// The results of the analysis are stored in instance +// variables of the Shader Generator. +//////////////////////////////////////////////////////////////////// +void ShaderGenerator:: +analyze_renderstate(const RenderState *rs) { + clear_analysis(); + + rs->store_into_slots(&_attribs); + + // Count number of textures. + _num_textures = 0; + if (_attribs._texture) { + _num_textures = _attribs._texture->get_num_on_stages(); + } + + // Determine whether or not vertex colors or flat colors are present. + if (_attribs._color != 0) { + if (_attribs._color->get_color_type() == ColorAttrib::T_vertex) { + _vertex_colors = true; + } else if (_attribs._color->get_color_type() == ColorAttrib::T_flat) { + _flat_colors = true; + } + } + + // Break out the lights by type. + + const LightAttrib *la = _attribs._light; + 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); + } + else if (light_obj->get_type() == DirectionalLight::get_class_type()) { + _dlights_np.push_back(light); + _dlights.push_back((DirectionalLight*)light_obj); + } + else if (light_obj->get_type() == PointLight::get_class_type()) { + _plights_np.push_back(light); + _plights.push_back((PointLight*)light_obj); + } + else if (light_obj->get_type() == Spotlight::get_class_type()) { + _slights_np.push_back(light); + _slights.push_back((Spotlight*)light_obj); + } + } + + // Determine whether model-space or tangent-space lighting is recommended. + + if (_attribs._light->get_num_on_lights() > 0) { + _ms_lighting = true; + } + + // Find the material. + + if (!_attribs._material->is_off()) { + _material = _attribs._material->get_material(); + } else { + _material = Material::get_default(); + } + + // Decide which material modes need to be calculated. + + if (_alights.size() > 0) { + if (_material->has_ambient()) { + Colorf a = _material->get_ambient(); + if ((a[0]!=0.0)||(a[1]!=0.0)||(a[2]!=0.0)) { + _have_ambient = true; + } + } else { + _have_ambient = true; + } + } + + if (_dlights.size() + _plights.size() + _slights.size()) { + if (_material->has_diffuse()) { + Colorf d = _material->get_diffuse(); + if ((d[0]!=0.0)||(d[1]!=0.0)||(d[2]!=0.0)) { + _have_diffuse = true; + } + } else { + _have_diffuse = true; + } + } + + if (_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 (_dlights.size() + _plights.size() + _slights.size()) { + if (_material->has_specular()) { + Colorf s = _material->get_specular(); + if ((s[0]!=0.0)||(s[1]!=0.0)||(s[2]!=0.0)) { + _have_specular = true; + } + } + } + + // Does the shader need material properties as input? + + _need_material_props = + (_have_ambient && (_material->has_ambient()))|| + (_have_diffuse && (_material->has_diffuse()))|| + (_have_emission && (_material->has_emission()))|| + (_have_specular && (_material->has_specular())); +} + + + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::clear_analysis +// Access: Protected +// Description: Called after analyze_renderstate to discard all +// the results of the analysis. This is generally done +// after shader generation is complete. +//////////////////////////////////////////////////////////////////// +void ShaderGenerator:: +clear_analysis() { + _vertex_colors = false; + _flat_colors = false; + _ms_lighting = false; + _ts_lighting = false; + _have_ambient = false; + _have_diffuse = false; + _have_emission = false; + _have_specular = false; + _attribs.clear_to_defaults(); + _material = (Material*)NULL; + _alights.clear(); + _dlights.clear(); + _plights.clear(); + _slights.clear(); + _alights_np.clear(); + _dlights_np.clear(); + _plights_np.clear(); + _slights_np.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: ShaderGenerator::create_shader_attrib +// Access: Protected +// Description: Creates a ShaderAttrib given a generated shader's +// body. Also inserts the lights into the shader +// attrib. +//////////////////////////////////////////////////////////////////// +CPT(RenderAttrib) ShaderGenerator:: +create_shader_attrib(const string &txt) { + PT(Shader) shader = Shader::make(txt); + CPT(RenderAttrib) shattr = ShaderAttrib::make(); + shattr=DCAST(ShaderAttrib, shattr)->set_shader(shader); + if (_ms_lighting) { + for (int i=0; i<(int)_alights.size(); i++) { + shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("alight", i), _alights_np[i]); + } + for (int i=0; i<(int)_dlights.size(); i++) { + shattr=DCAST(ShaderAttrib, shattr)->set_shader_input(InternalName::make("dlight", i), _dlights_np[i]); + } + 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]); + } + } + return shattr; +} //////////////////////////////////////////////////////////////////// // Function: ShaderGenerator::synthesize_shader -// Access: Public, Static +// Access: Published, Virtual // Description: This is the routine that implements the next-gen // fixed function pipeline by synthesizing a shader. // // Currently supports: -// - textures -// - materials +// - 2D textures // - lights +// - vertex colors +// - flat colors +// - materials // // Not yet supported: -// - vertex colors +// - 3D textures, cube textures +// - color scale attrib // - texgen // - texmatrix -// - separate normal and gloss +// - normal and gloss // - lots of other things // //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) ShaderGenerator:: synthesize_shader(const RenderState *rs) { - AttribSlots attribs; - rs->store_into_slots(&attribs); - - int num_textures = 0; - if (attribs._texture) { - num_textures = attribs._texture->get_num_on_stages(); + analyze_renderstate(rs); + reset_register_allocator(); + + // These variables will hold the results of register allocation. + + char *pos_freg = 0; + char *normal_vreg = 0; + char *tangent_vreg = 0; + char *binormal_vreg = 0; + char *normal_freg = 0; + char *eyevec_freg = 0; + pvector texcoord_vreg; + pvector texcoord_freg; + pvector tslightvec_freg; + + if (_vertex_colors) { + _vcregs_used = 1; + _fcregs_used = 1; } - ostringstream text; - CPT(RenderAttrib) rattrib = ShaderAttrib::make(); + // Generate the shader's text. + ostringstream text; + text << "//Cg\n"; text << "void vshader(\n"; - for (int i=0; iget_local()) { + text << "\t uniform float4 mspos_view,\n"; + } else { + text << "\t uniform float4 row1_view_to_model,\n"; + } + } + } text << "\t out float4 o_color : COLOR\n"; text << ") {\n"; - text << "\t o_color = float4(1,1,1,1);\n"; - for (int i=0; iget_local()) { + text << "\t lhalf = normalize(normalize(mspos_view - l_pos) + lvec);\n"; + } else { + text << "\t lhalf = dlight_dlight" << i << "_rel_model[3];\n"; + } + text << "\t lspec *= pow(saturate(dot(l_normal, lhalf.xyz)), attr_material[3].w);\n"; + text << "\t tot_specular += lspec;\n"; + } + } + for (int i=0; i<(int)_plights.size(); i++) { + text << "\t // Point Light " << i << "\n"; + text << "\t lcolor = plight_plight" << i << "_rel_model[0];\n"; + text << "\t lspec = plight_plight" << i << "_rel_model[1];\n"; + text << "\t lpoint = plight_plight" << i << "_rel_model[2];\n"; + text << "\t latten = plight_plight" << i << "_rel_model[3];\n"; + text << "\t lvec = lpoint - l_pos;\n"; + text << "\t ldist = length(lvec);\n"; + text << "\t lvec /= ldist;\n"; + text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n"; + text << "\t lcolor *= lattenv * saturate(dot(l_normal, lvec.xyz));\n"; + text << "\t tot_diffuse += lcolor;\n"; + if (_have_specular) { + if (_material->get_local()) { + text << "\t lhalf = normalize(normalize(mspos_view - l_pos) + lvec);\n"; + } else { + text << "\t lhalf = normalize(lvec - row1_view_to_model);\n"; + } + text << "\t lspec *= lattenv;\n"; + text << "\t lspec *= pow(saturate(dot(l_normal, lhalf.xyz)), attr_material[3].w);\n"; + text << "\t tot_specular += lspec;\n"; + } + } + for (int i=0; i<(int)_slights.size(); i++) { + text << "\t // Spot Light " << i << "\n"; + text << "\t lcolor = slight_slight" << i << "_rel_model[0];\n"; + text << "\t lspec = slight_slight" << i << "_rel_model[1];\n"; + text << "\t lpoint = slight_slight" << i << "_rel_model[2];\n"; + text << "\t ldir = slight_slight" << i << "_rel_model[3];\n"; + text << "\t latten = satten_slight" << i << ";\n"; + text << "\t lvec = lpoint - l_pos;\n"; + text << "\t ldist = length(lvec);\n"; + text << "\t lvec /= ldist;\n"; + text << "\t langle = saturate(dot(ldir.xyz, lvec.xyz));\n"; + text << "\t lattenv = 1/(latten.x + latten.y*ldist + latten.z*ldist*ldist);\n"; + 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"; + text << "\t tot_diffuse += lcolor;\n"; + if (_have_specular) { + if (_material->get_local()) { + text << "\t lhalf = normalize(normalize(mspos_view - l_pos) + lvec);\n"; + } else { + text << "\t lhalf = normalize(lvec - row1_view_to_model);\n"; + } + text << "\t lspec *= lattenv;\n"; + text << "\t lspec *= pow(saturate(dot(l_normal, lhalf.xyz)), attr_material[3].w);\n"; + text << "\t tot_specular += lspec;\n"; + } + } + if (_have_emission) { + text << "\t o_color = attr_material[2];\n"; + } else { + text << "\t o_color = float4(0,0,0,1);\n"; + } + if (_have_ambient) { + if (_material->has_ambient()) { + text << "\t o_color += tot_ambient * attr_material[0];\n"; + } else if (_vertex_colors) { + text << "\t o_color += tot_ambient * l_color;\n"; + } else if (_flat_colors) { + text << "\t o_color += tot_ambient * attr_color;\n"; + } else { + text << "\t o_color += tot_ambient;\n"; + } + } + if (_have_diffuse) { + if (_material->has_diffuse()) { + text << "\t o_color += tot_diffuse * attr_material[1];\n"; + } else if (_vertex_colors) { + text << "\t o_color += tot_diffuse * l_color;\n"; + } else if (_flat_colors) { + text << "\t o_color += tot_diffuse * attr_color;\n"; + } else { + text << "\t o_color += tot_diffuse;\n"; + } + } + } else { + if (_vertex_colors) { + text << "\t o_color = l_color;\n"; + } else if (_flat_colors) { + text << "\t o_color = attr_color;\n"; + } else { + text << "\t o_color = float4(1,1,1,1);\n"; + } + } + for (int i=0; i<_num_textures; i++) { + text << "\t o_color *= tex2D(tex_" << i << ", float2(l_texcoord" << i << "));\n"; + } + if (_ms_lighting) { + if (_have_specular) { + text << "\t o_color += tot_specular;\n"; + } + } + text << "}\n"; + // Insert the shader into the shader attrib. - PT(Shader) shader = Shader::make(text.str()); - rattrib = DCAST(ShaderAttrib, rattrib)->set_shader(shader); - return rattrib; + CPT(RenderAttrib) shattr = create_shader_attrib(text.str()); + clear_analysis(); + reset_register_allocator(); + return shattr; } diff --git a/panda/src/pgraph/shaderGenerator.h b/panda/src/pgraph/shaderGenerator.h index b99a9d68dc..a910c09408 100644 --- a/panda/src/pgraph/shaderGenerator.h +++ b/panda/src/pgraph/shaderGenerator.h @@ -20,6 +20,14 @@ #define SHADERGENERATOR_H #include "pandabase.h" +#include "typedWritableReferenceCount.h" +#include "nodePath.h" +class AmbientLight; +class DirectionalLight; +class PointLight; +class Spotlight; +class LightAttrib; +class ShaderAttrib; //////////////////////////////////////////////////////////////////// // Class : ShaderGenerator @@ -31,6 +39,11 @@ // 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. +// // The ShaderGenerator owes its existence to the // 'Bamboo Team' at Carnegie Mellon's Entertainment // Technology Center. This is a group of students @@ -49,13 +62,77 @@ // //////////////////////////////////////////////////////////////////// -class EXPCL_PANDA_PGRAPH ShaderGenerator { +class EXPCL_PANDA_PGRAPH ShaderGenerator : public TypedWritableReferenceCount { private: - INLINE ShaderGenerator(); - INLINE ~ShaderGenerator(); + static PT(ShaderGenerator) _default_generator; + +PUBLISHED: + ShaderGenerator(); + virtual ~ShaderGenerator(); + static ShaderGenerator *get_default(); + static void set_default(ShaderGenerator *generator); + virtual CPT(RenderAttrib) synthesize_shader(const RenderState *rs); + +protected: + CPT(RenderAttrib) create_shader_attrib(const string &txt); + + // Shader register allocation: + + int _vcregs_used; + int _fcregs_used; + int _vtregs_used; + int _ftregs_used; + void reset_register_allocator(); + INLINE char *alloc_vreg(); + INLINE char *alloc_freg(); + + // RenderState analysis information. Created by analyze_renderstate: + + AttribSlots _attribs; + Material *_material; + int _num_textures; + + pvector _alights; + pvector _dlights; + pvector _plights; + pvector _slights; + pvector _alights_np; + pvector _dlights_np; + pvector _plights_np; + pvector _slights_np; + + bool _vertex_colors; + bool _flat_colors; + + bool _ms_lighting; + bool _ts_lighting; + + bool _have_ambient; + bool _have_diffuse; + bool _have_emission; + bool _have_specular; + + bool _need_material_props; + + void analyze_renderstate(const RenderState *rs); + void clear_analysis(); public: - static CPT(RenderAttrib) synthesize_shader(const RenderState *rs); + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedReferenceCount::init_type(); + register_type(_type_handle, "ShaderGenerator", + TypedReferenceCount::get_class_type()); + } + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + + private: + static TypeHandle _type_handle; }; #include "shaderGenerator.I"