1 #ifndef __ShaderSystemMultiLight_H__ 2 #define __ShaderSystemMultiLight_H__ 3 4 #include "SdkSample.h" 5 #include "SegmentedDynamicLightManager.h" 6 #include "RTShaderSRSSegmentedLights.h" 7 #include "OgreControllerManager.h" 8 9 /* 10 Part of the original guidelines under which the RTSS was created was to emulate the fixed pipeline mechanism as close as possible. 11 Due to this fact and how it was interpreted using multiple lights in RTSS with the default implementation is problematic. Every light 12 requires it's own line in the shader. Every time an object receives a different amount of lights the shader for is invalidated and lights 13 recompiled. Amount of is also limited by the amount of const registers a shader supports. 14 15 The following example shows a different approach to rendering lights in RTSS. A few points on this system 16 - Only one directional light is supported. 17 - Point lights and spot lights are handled through the same code. 18 - Light attenuation is only controlled by range. all other parameters are ignored (to produce more efficient shader programs) 19 - point light specular effect is not calculated (to produce more faster shader programs). If any one wants to add it feel free. 20 - Large amount of lights can be supported. Limited currently by the size of the texture used to send the light information to the 21 shader (currently set to a 9x9 grid. each grid cell can contain 32 lights). 22 - No need to recompile the shader when the number of lights on an object changes 23 - Sample requires shader model 3 or higher to run in order 24 - The world is divided into a grid of 9x9 cells (can be easily increased). Each cell receives it's own list of lights appropriate 25 only for it. This can be increased depending on your situation. 26 - The information of the lights in the grid is transferred onto a texture. Which is sent to the shader. 27 - The list of lights is iterated over in the shader through a dynamic loop. 28 29 30 Note: 31 This code was somewhat inspired by Kojack's "Tons of street lights" (http://www.ogre3d.org/forums/viewtopic.php?t=48412) idea. One of 32 the more innovative ideas I've seen of late. 33 34 */ 35 36 using namespace Ogre; 37 using namespace OgreBites; 38 39 const uint8 cPriorityMain = 50; 40 const uint8 cPriorityQuery = 51; 41 const uint8 cPriorityLights = 55; 42 const unsigned int cInitialLightCount = 3; 43 const String DEBUG_MODE_CHECKBOX = "DebugMode"; 44 const String NUM_OF_LIGHTS_SLIDER = "NumOfLights"; 45 const String TWIRL_LIGHTS_CHECKBOX = "TwirlLights"; 46 47 48 class _OgreSampleClassExport Sample_ShaderSystemMultiLight : public SdkSample 49 { 50 public: 51 Sample_ShaderSystemMultiLight()52 Sample_ShaderSystemMultiLight() : 53 mTwirlLights(false), 54 mSRSSegLightFactory(NULL), 55 mPathNameGen("RTPath") 56 { 57 mInfo["Title"] = "ShaderSystem - Multi Light"; 58 mInfo["Description"] = "Shows a possible way to support a large varying amount of spot lights in the RTSS using a relatively simple system." 59 "Note in debug mode green and red lines show the light grid. Blue shows the amount of lights processed per grid position."; 60 mInfo["Thumbnail"] = "thumb_shadersystemmultilight.png"; 61 mInfo["Category"] = "Lighting"; 62 63 } 64 ~Sample_ShaderSystemMultiLight()65 ~Sample_ShaderSystemMultiLight() 66 { 67 68 } 69 _shutdown()70 virtual void _shutdown() 71 { 72 delete SegmentedDynamicLightManager::getSingletonPtr(); 73 74 RTShader::RenderState* pMainRenderState = 75 RTShader::ShaderGenerator::getSingleton().createOrRetrieveRenderState(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME).first; 76 pMainRenderState->reset(); 77 78 if (mSRSSegLightFactory) 79 { 80 RTShader::ShaderGenerator::getSingleton().removeAllShaderBasedTechniques(); 81 RTShader::ShaderGenerator::getSingleton().removeSubRenderStateFactory(mSRSSegLightFactory); 82 delete mSRSSegLightFactory; 83 mSRSSegLightFactory = NULL; 84 } 85 86 while (mLights.size()) 87 { 88 removeSpotLight(); 89 } 90 91 SdkSample::_shutdown(); 92 } 93 frameRenderingQueued(const FrameEvent & evt)94 bool frameRenderingQueued(const FrameEvent& evt) 95 { 96 // Move the lights along their paths 97 for(size_t i = 0 ; i < mLights.size() ; ++i) 98 { 99 mLights[i].animState->addTime(evt.timeSinceLastFrame); 100 if (mTwirlLights) 101 { 102 mLights[i].light->setDirection( 103 Quaternion(Degree(ControllerManager::getSingleton().getElapsedTime() * 150 + 360 * i / (float)mLights.size()), Vector3::UNIT_Y) * 104 Vector3(0,-1,-1).normalisedCopy()); 105 } 106 else 107 { 108 mLights[i].light->setDirection(Vector3::NEGATIVE_UNIT_Y); 109 } 110 } 111 112 113 return SdkSample::frameRenderingQueued(evt); // don't forget the parent class updates! 114 } 115 116 protected: 117 testCapabilities(const RenderSystemCapabilities * caps)118 void testCapabilities( const RenderSystemCapabilities* caps ) 119 { 120 if (!Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_3_0") && 121 !Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_4_0") && 122 !Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_4_1") && 123 !Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("ps_5_0") && 124 !Ogre::Root::getSingletonPtr()->getRenderSystem()->getCapabilities()->isShaderProfileSupported("glsl")) 125 { 126 OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "This sample uses dynamic loops in Cg or GLSL type shader language, your graphic card must support Shader Profile 3 or above." 127 " You cannot run this sample. Sorry!", "Sample_ShaderSystemMultiLight::testCapabilities"); 128 } 129 } 130 setupContent()131 void setupContent() 132 { 133 mTrayMgr->createThickSlider(TL_BOTTOM, NUM_OF_LIGHTS_SLIDER, "Num of lights", 240, 80, 0, 64, 65)->setValue(cInitialLightCount, false); 134 mTrayMgr->createCheckBox(TL_BOTTOM, TWIRL_LIGHTS_CHECKBOX, "Twirl Lights", 240)->setChecked(false, false); 135 mTrayMgr->createCheckBox(TL_BOTTOM, DEBUG_MODE_CHECKBOX, "Show Grid", 240)->setChecked(false, false); 136 137 // Set our camera to orbit around the origin at a suitable distance 138 mCamera->setPosition(0, 100, 600); 139 140 mTrayMgr->showCursor(); 141 142 // create a floor mesh resource 143 MeshManager::getSingleton().createPlane("floor", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, 144 Plane(Vector3::UNIT_Y, -30), 1000, 1000, 10, 10, true, 1, 8, 8, Vector3::UNIT_Z); 145 146 // create a floor entity, give it a material, and place it at the origin 147 Entity* floor = mSceneMgr->createEntity("Floor", "floor"); 148 floor->setMaterialName("Examples/BumpyMetal"); 149 mSceneMgr->getRootSceneNode()->attachObject(floor); 150 151 // Create an ogre head and place it at the origin 152 Entity* head = mSceneMgr->createEntity("Head", "ogrehead.mesh"); 153 head->setRenderQueueGroup(cPriorityMain); 154 mSceneMgr->getRootSceneNode()->attachObject(head); 155 156 setupShaderGenerator(); 157 158 setupLights(); 159 } 160 setupShaderGenerator()161 void setupShaderGenerator() 162 { 163 new SegmentedDynamicLightManager; 164 165 SegmentedDynamicLightManager::getSingleton().setSceneManager(mSceneMgr); 166 167 RTShader::ShaderGenerator* mGen = RTShader::ShaderGenerator::getSingletonPtr(); 168 169 RTShader::RenderState* pMainRenderState = 170 mGen->createOrRetrieveRenderState(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME).first; 171 pMainRenderState->reset(); 172 173 // If we are using segmented lighting, no auto light update required. (prevent constant invalidation) 174 pMainRenderState->setLightCountAutoUpdate(false); 175 176 mSRSSegLightFactory = new RTShaderSRSSegmentedLightsFactory; 177 mGen->addSubRenderStateFactory(mSRSSegLightFactory); 178 pMainRenderState->addTemplateSubRenderState( 179 mGen->createSubRenderState(RTShaderSRSSegmentedLights::Type)); 180 181 182 mGen->invalidateScheme(Ogre::RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); 183 184 // Make this viewport work with shader generator scheme. 185 mViewport->setMaterialScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); 186 } 187 188 setupLights()189 void setupLights() 190 { 191 mSceneMgr->setAmbientLight(ColourValue(0.1, 0.1, 0.1)); 192 // set the single directional light 193 Light* light = mSceneMgr->createLight(); 194 light->setType(Light::LT_DIRECTIONAL); 195 light->setDirection(Vector3(-1,-1,0).normalisedCopy()); 196 light->setDiffuseColour(ColourValue(0.1, 0.1, 0.1)); 197 light->setCastShadows(false); 198 199 for(unsigned int i = 0 ; i < cInitialLightCount ; ++i) 200 { 201 addSpotLight(); 202 } 203 } 204 addSpotLight()205 void addSpotLight() 206 { 207 LightState state; 208 209 // Create a light node 210 state.node = mSceneMgr->getRootSceneNode()->createChildSceneNode(Vector3(50, 30, 0)); 211 212 String animName = mPathNameGen.generate(); 213 // Create a 14 second animation with spline interpolation 214 215 const int animPoints = 5; 216 const int animTimeBetweenPoints = 4; 217 state.anim = mSceneMgr->createAnimation(animName, animPoints * animTimeBetweenPoints); 218 state.anim->setInterpolationMode(Animation::IM_SPLINE); 219 220 state.track = state.anim->createNodeTrack(1, state.node); // Create a node track for our animation 221 222 // Enter keyframes for our track to define a path for the light to follow 223 Vector3 firstFramePos; 224 for(int i = 0 ; i <= animPoints ; ++i) 225 { 226 Vector3 framePos(rand01() * 900 - 500, 10 + rand01() * 100, rand01() * 900 - 500); 227 if (i == 0) 228 { 229 firstFramePos = framePos; 230 } 231 if (i == animPoints) 232 { 233 framePos = firstFramePos; 234 } 235 state.track->createNodeKeyFrame(i * animTimeBetweenPoints)->setTranslate(framePos); 236 } 237 238 239 ColourValue lightColor(rand01(), rand01(), rand01()); 240 float complement = 1 - std::max<float>(std::max<float>(lightColor.r, lightColor.g), lightColor.b); 241 lightColor.r += complement; 242 lightColor.g += complement; 243 lightColor.b += complement; 244 245 // Create an animation state from the animation and enable it 246 state.animState = mSceneMgr->createAnimationState(animName); 247 state.animState->setEnabled(true); 248 249 // Attach a light with the same colour to the light node 250 state.light = mSceneMgr->createLight(); 251 state.light->setCastShadows(false); 252 state.light->setType(mLights.size() % 10 ? Light::LT_SPOTLIGHT : Light::LT_POINT); 253 state.light->setDirection(Vector3::NEGATIVE_UNIT_Y); 254 state.light->setAttenuation(200,0,0,0); 255 state.light->setDiffuseColour(lightColor); 256 state.node->attachObject(state.light); 257 258 // Attach a flare with the same colour to the light node 259 state.bbs = mSceneMgr->createBillboardSet(1); 260 Billboard* bb = state.bbs->createBillboard(Vector3::ZERO, lightColor); 261 bb->setColour(lightColor); 262 state.bbs->setMaterialName("Examples/Flare"); 263 state.bbs->setRenderQueueGroup(cPriorityLights); 264 state.node->attachObject(state.bbs); 265 266 mLights.push_back(state); 267 } 268 rand01()269 float rand01() 270 { 271 return (abs(rand()) % 1000) / 1000.0f; 272 } 273 setDebugModeState(bool state)274 void setDebugModeState(bool state) 275 { 276 bool needInvalidate = SegmentedDynamicLightManager::getSingleton().setDebugMode(state); 277 if (needInvalidate) 278 { 279 RTShader::ShaderGenerator::getSingleton().invalidateScheme(RTShader::ShaderGenerator::DEFAULT_SCHEME_NAME); 280 } 281 } 282 283 //-------------------------------------------------------------------------- sliderMoved(Slider * slider)284 void sliderMoved(Slider* slider) 285 { 286 if (slider->getName() == NUM_OF_LIGHTS_SLIDER) 287 { 288 size_t numOfLights = (size_t)slider->getValue(); 289 290 while (mLights.size() < numOfLights) 291 { 292 addSpotLight(); 293 } 294 295 while (numOfLights < mLights.size()) 296 { 297 removeSpotLight(); 298 } 299 } 300 } 301 removeSpotLight()302 void removeSpotLight() 303 { 304 if (!mLights.empty()) 305 { 306 LightState& state = mLights[mLights.size() - 1]; 307 308 // Delete the nodes 309 mSceneMgr->destroyBillboardSet(state.bbs); 310 mSceneMgr->destroyLight(state.light); 311 mSceneMgr->destroyAnimationState(state.anim->getName()); 312 mSceneMgr->destroyAnimation(state.anim->getName()); 313 mSceneMgr->destroySceneNode(state.node); 314 315 316 mLights.resize(mLights.size() - 1); 317 } 318 } 319 320 checkBoxToggled(CheckBox * box)321 void checkBoxToggled(CheckBox* box) 322 { 323 const String& cbName = box->getName(); 324 325 if (cbName == DEBUG_MODE_CHECKBOX) 326 { 327 setDebugModeState(box->isChecked()); 328 } 329 if (cbName == TWIRL_LIGHTS_CHECKBOX) 330 { 331 mTwirlLights = box->isChecked(); 332 } 333 } 334 335 #if (OGRE_PLATFORM != OGRE_PLATFORM_APPLE_IOS) && (OGRE_PLATFORM != OGRE_PLATFORM_ANDROID) 336 337 //----------------------------------------------------------------------- mousePressed(const OIS::MouseEvent & evt,OIS::MouseButtonID id)338 bool mousePressed( const OIS::MouseEvent& evt, OIS::MouseButtonID id ) 339 { 340 if (mTrayMgr->injectMouseDown(evt, id)) 341 return true; 342 if (id == OIS::MB_Left) 343 mTrayMgr->hideCursor(); // hide the cursor if user left-clicks in the scene 344 345 return true; 346 } 347 348 //----------------------------------------------------------------------- mouseReleased(const OIS::MouseEvent & evt,OIS::MouseButtonID id)349 bool mouseReleased( const OIS::MouseEvent& evt, OIS::MouseButtonID id ) 350 { 351 if (mTrayMgr->injectMouseUp(evt, id)) 352 return true; 353 if (id == OIS::MB_Left) 354 mTrayMgr->showCursor(); // unhide the cursor if user lets go of LMB 355 356 return true; 357 } 358 359 //----------------------------------------------------------------------- mouseMoved(const OIS::MouseEvent & evt)360 bool mouseMoved( const OIS::MouseEvent& evt ) 361 { 362 // only rotate the camera if cursor is hidden 363 if (mTrayMgr->isCursorVisible()) 364 mTrayMgr->injectMouseMove(evt); 365 else 366 mCameraMan->injectMouseMove(evt); 367 368 369 return true; 370 } 371 #endif 372 373 374 375 private: 376 377 struct LightState 378 { 379 SceneNode* node; 380 Animation* anim; 381 NodeAnimationTrack* track; 382 AnimationState* animState; 383 Light* light; 384 BillboardSet* bbs; 385 }; 386 387 typedef Ogre::vector<LightState>::type VecLights; 388 VecLights mLights; 389 bool mTwirlLights; 390 391 RTShaderSRSSegmentedLightsFactory* mSRSSegLightFactory; 392 393 NameGenerator mPathNameGen; 394 }; 395 396 #endif 397