1 /*
2 -----------------------------------------------------------------------------
3 This source file is part of OGRE
4 (Object-oriented Graphics Rendering Engine)
5 For the latest info, see http://www.ogre3d.org/
6 
7 Copyright (c) 2000-2014 Torus Knot Software Ltd
8 Also see acknowledgements in Readme.html
9 
10 You may use this sample code for anything you like, it is not covered by the
11 same license as the rest of the engine.
12 -----------------------------------------------------------------------------
13 */
14 
15 /**
16 Implementation of a Deferred Shading engine in OGRE, using Multiple Render Targets and
17 CG high level language shaders.
18     // W.J. :wumpus: van der Laan 2005 / Noam Gat 2009 //
19 
20 Deferred shading renders the scene to a 'fat' texture format, using a shader that outputs colour,
21 normal, depth, and possible other attributes per fragment. Multi Render Target is required as we
22 are dealing with many outputs which get written into multiple render textures in the same pass.
23 
24 After rendering the scene in this format, the shading (lighting) can be done as a post process.
25 This means that lighting is done in screen space, using light-representing geometry (sphere for
26 point light, cone for spot light and quad for directional) to render their contribution.
27 
28 The wiki article explaining this demo can be found here :
29   http://www.ogre3d.org/wiki/index.php/Deferred_Shading
30 */
31 
32 #ifndef H_DeferredShadingDemo
33 #define H_DeferredShadingDemo
34 
35 #include "SdkSample.h"
36 
37 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
38 #define WIN32_LEAN_AND_MEAN
39 #include "windows.h"
40 #endif
41 
42 #include "SharedData.h"
43 #include "OgreCompositorInstance.h"
44 #include "OgreSceneManager.h"
45 #include "OgreSceneNode.h"
46 #include "OgreMaterial.h"
47 #include "OgreRenderTargetListener.h"
48 #include "GeomUtils.h"
49 
50 using namespace Ogre;
51 using namespace OgreBites;
52 
53 const ColourValue SAMPLE_COLORS[] =
54 {   ColourValue::Red, ColourValue::Green, ColourValue::Blue,
55     ColourValue::White, ColourValue(1,1,0,1), ColourValue(1,0,1,1)
56 };
57 
58 class _OgreSampleClassExport Sample_DeferredShading : public SdkSample, public RenderTargetListener
59 {
60 protected:
61     DeferredShadingSystem *mSystem;
62     SelectMenu* mDisplayModeMenu;
63 
64 public:
Sample_DeferredShading()65     Sample_DeferredShading()
66     {
67         mInfo["Title"] = "Deferred Shading";
68         mInfo["Description"] = "A sample implementation of a deferred renderer using the compositor framework.";
69         mInfo["Thumbnail"] = "thumb_deferred.png";
70         mInfo["Category"] = "Lighting";
71         mInfo["Help"] = "See http://www.ogre3d.org/wiki/index.php/Deferred_Shading for more info";
72     }
73 
74 protected:
75 
cleanupContent(void)76     void cleanupContent(void)
77     {
78         delete ( SharedData::getSingletonPtr() );
79 
80         delete mSystem;
81     }
82 
setupControls()83     void setupControls()
84     {
85         mTrayMgr->showCursor();
86 
87         // create checkboxs to toggle ssao and shadows
88         mTrayMgr->createCheckBox(TL_TOPLEFT, "DeferredShading", "Deferred Shading", 220)->setChecked(true, false);
89         mTrayMgr->createCheckBox(TL_TOPLEFT, "SSAO", "Ambient Occlusion", 220)->setChecked(false, false);
90         mTrayMgr->createCheckBox(TL_TOPLEFT, "GlobalLight", "Global Light", 220)->setChecked(true, false);
91         mTrayMgr->createCheckBox(TL_TOPLEFT, "Shadows", "Shadows", 220)->setChecked(true, false);
92 
93         // create a menu to choose the model displayed
94         mDisplayModeMenu = mTrayMgr->createThickSelectMenu(TL_TOPLEFT, "DisplayMode", "Display Mode", 220, 4);
95         mDisplayModeMenu->addItem("Regular view");
96         mDisplayModeMenu->addItem("Debug colours");
97         mDisplayModeMenu->addItem("Debug normals");
98         mDisplayModeMenu->addItem("Debug depth / specular");
99     }
100 
itemSelected(SelectMenu * menu)101     void itemSelected(SelectMenu* menu)
102     {
103         //Options are aligned with the mode enum
104         SharedData::getSingleton().iSystem->setMode(
105                                                     (DeferredShadingSystem::DSMode)menu->getSelectionIndex());
106     }
107 
checkBoxToggled(CheckBox * box)108     void checkBoxToggled(CheckBox* box)
109     {
110         if (box->getName() == "SSAO")
111         {
112             SharedData::getSingleton().iSystem->setSSAO(box->isChecked());
113         }
114         else if (box->getName() == "GlobalLight")
115         {
116             SharedData::getSingleton().iGlobalActivate = box->isChecked();
117             SharedData::getSingleton().iMainLight->setVisible(box->isChecked());
118         }
119         else if (box->getName() == "Shadows")
120         {
121             mSceneMgr->setShadowTechnique(box->isChecked() ?
122                                           SHADOWTYPE_TEXTURE_ADDITIVE :
123                                           SHADOWTYPE_NONE);
124         }
125         else if (box->getName() == "DeferredShading")
126         {
127             SharedData::getSingleton().iSystem->setActive(box->isChecked());
128         }
129     }
130 
131     //Utility function to help set scene up
setEntityHeight(Entity * ent,Real newHeight)132     void setEntityHeight(Entity* ent, Real newHeight)
133     {
134         Real curHeight = ent->getMesh()->getBounds().getSize().y;
135         Real scaleFactor = newHeight / curHeight;
136 
137         SceneNode* parentNode = ent->getParentSceneNode();
138         parentNode->setScale(scaleFactor, scaleFactor, scaleFactor);
139     }
140 
createAtheneScene(SceneNode * rootNode)141     void createAtheneScene(SceneNode* rootNode)
142     {
143         // Prepare athene mesh for normalmapping
144         MeshPtr pAthene = MeshManager::getSingleton().load("athene.mesh",
145                                                            ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
146         unsigned short src, dest;
147         if (!pAthene->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
148             pAthene->buildTangentVectors(VES_TANGENT, src, dest);
149 
150         //Create an athena statue
151         Entity* athena = mSceneMgr->createEntity("Athena", "athene.mesh");
152         athena->setMaterialName("DeferredDemo/DeferredAthena");
153         SceneNode *aNode = rootNode->createChildSceneNode();
154         aNode->attachObject( athena );
155         aNode->setPosition(-8.5, 4.5, 0);
156         setEntityHeight(athena, 4.0);
157         aNode->yaw(Ogre::Degree(90));
158         // Create some happy little lights to decorate the athena statue
159         createSampleLights();
160     }
161 
createKnotScene(SceneNode * rootNode)162     void createKnotScene(SceneNode* rootNode)
163     {
164         // Prepare knot mesh for normal mapping
165         MeshPtr pKnot = MeshManager::getSingleton().load("knot.mesh",
166                                                          ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
167         unsigned short src, dest;
168         if (!pKnot->suggestTangentVectorBuildParams(VES_TANGENT, src, dest))
169             pKnot->buildTangentVectors(VES_TANGENT, src, dest);
170 
171         // Create a bunch of knots with spotlights hanging from above
172         Entity* knotEnt = mSceneMgr->createEntity("Knot", "knot.mesh");
173         knotEnt->setMaterialName("DeferredDemo/RockWall");
174         //knotEnt->setMeshLodBias(0.25f);
175         Vector3 knotStartPos(25.5, 2, 5.5);
176         Vector3 knotDiff(-3.7, 0, 0);
177         for (int i=0; i < 5; i++)
178         {
179             Entity* cloneKnot = knotEnt->clone(StringUtil::format("Knot%d", i));
180             Vector3 clonePos = knotStartPos + knotDiff*i;
181             SceneNode* cloneNode = rootNode->createChildSceneNode(clonePos);
182             cloneNode->attachObject(cloneKnot);
183             setEntityHeight(cloneKnot, 3);
184             cloneNode->yaw(Degree(i*17));
185             cloneNode->roll(Degree(i*31));
186 
187             Light* knotLight = mSceneMgr->createLight(StringUtil::format("KnotLight%d", i));
188             SceneNode* ln = rootNode->createChildSceneNode(clonePos + Vector3(0,3,0));
189             ln->setDirection(Vector3::NEGATIVE_UNIT_Y);
190             ln->attachObject(knotLight);
191             knotLight->setType(Light::LT_SPOTLIGHT);
192             knotLight->setDiffuseColour(SAMPLE_COLORS[i]);
193             knotLight->setSpecularColour(ColourValue::White);
194             knotLight->setSpotlightRange(Degree(25), Degree(45), 1);
195             knotLight->setAttenuation(6, 1, 0.2, 0);
196         }
197     }
198 
createObjects(SceneNode * rootNode)199     void createObjects(SceneNode* rootNode)
200     {
201         // Create ogre heads to decorate the wall
202         Entity* ogreHead = mSceneMgr->createEntity("Head", "ogrehead.mesh");
203         //rootNode->createChildSceneNode( "Head" )->attachObject( ogreHead );
204         Vector3 headStartPos[2] = { Vector3(25.25,11,3), Vector3(25.25,11,-3) };
205         Vector3 headDiff(-3.7,0,0);
206         for (int i=0; i < 12; i++)
207         {
208             Entity* cloneHead = ogreHead->clone(StringUtil::format("OgreHead%d", i));
209             Vector3 clonePos = headStartPos[i%2] + headDiff*(i/2);
210             if ((i/2) >= 4) clonePos.x -= 0.75;
211             SceneNode* cloneNode = rootNode->createChildSceneNode(clonePos);
212             cloneNode->attachObject(cloneHead);
213             setEntityHeight(cloneHead, 1.5);
214             if (i % 2 == 0)
215             {
216                 cloneNode->yaw(Degree(180));
217             }
218         }
219 
220         // Create a pile of wood pallets
221         Entity* woodPallet = mSceneMgr->createEntity("Pallet", "WoodPallet.mesh");
222         Vector3 woodStartPos(10, 0.5, -5.5);
223         Vector3 woodDiff(0, 0.3, 0);
224         for (int i=0; i < 5; i++)
225         {
226             Entity* clonePallet = woodPallet->clone(StringUtil::format("WoodPallet%d", i));
227             Vector3 clonePos = woodStartPos + woodDiff*i;
228             SceneNode* cloneNode = rootNode->createChildSceneNode(clonePos);
229             cloneNode->attachObject(clonePallet);
230             setEntityHeight(clonePallet, 0.3);
231             cloneNode->yaw(Degree(i*20));
232         }
233 
234     }
235 
getRequiredPlugins()236     StringVector getRequiredPlugins()
237     {
238         StringVector names;
239         if (!GpuProgramManager::getSingleton().isSyntaxSupported("glsles") && !GpuProgramManager::getSingleton().isSyntaxSupported("glsl150"))
240             names.push_back("Cg Program Manager");
241         return names;
242     }
243 
testCapabilities(const RenderSystemCapabilities * caps)244     void testCapabilities(const RenderSystemCapabilities* caps)
245     {
246         if (!caps->hasCapability(RSC_VERTEX_PROGRAM) || !(caps->hasCapability(RSC_FRAGMENT_PROGRAM)))
247         {
248             OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support vertex and fragment programs, so cannot "
249                         "run this demo. Sorry!",
250                         "DeferredShading::testCapabilities");
251         }
252         if (caps->getNumMultiRenderTargets()<2)
253         {
254             OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your card does not support at least two simultaneous render targets, so cannot "
255                         "run this demo. Sorry!",
256                         "DeferredShading::testCapabilities");
257         }
258 
259         if (!GpuProgramManager::getSingleton().isSyntaxSupported("vs_1_1") &&
260             !GpuProgramManager::getSingleton().isSyntaxSupported("arbvp1") &&
261             !GpuProgramManager::getSingleton().isSyntaxSupported("vs_4_0") &&
262             !GpuProgramManager::getSingleton().isSyntaxSupported("glsl300es") &&
263             !GpuProgramManager::getSingleton().isSyntaxSupported("glsl150"))
264         {
265             OGRE_EXCEPT(Exception::ERR_NOT_IMPLEMENTED, "Your graphics card does not support advanced vertex"
266                         " programs, so you cannot run this sample. Sorry!", "DeferredShading::testCapabilities");
267         }
268     }
269 
270     // Just override the mandatory create scene method
setupContent(void)271     void setupContent(void)
272     {
273         mCameraMan->setTopSpeed(20.0);
274         new SharedData();
275         mSystem = 0;
276 
277         // Set ambient light
278         mSceneMgr->setAmbientLight(ColourValue(0.15, 0.00, 0.00));
279         // Skybox
280         mSceneMgr->setSkyBox(true, "DeferredDemo/SkyBox", 500);
281         // Create main, static light
282         Light* l1 = mSceneMgr->createLight();
283         l1->setType(Light::LT_DIRECTIONAL);
284         l1->setDiffuseColour(0.5f, 0.45f, 0.1f);
285         l1->setDirection(1, -0.5, -0.2);
286         l1->setShadowFarClipDistance(250);
287         l1->setShadowFarDistance(75);
288         //Turn this on to have the directional light cast shadows
289         l1->setCastShadows(false);
290 
291         mCameraNode->setPosition(25, 5, 0);
292         mCameraNode->lookAt(Vector3::ZERO, Node::TS_PARENT);
293         mCamera->setFarClipDistance(1000.0);
294         mCamera->setNearClipDistance(0.5);
295         setDragLook(true);
296 
297         mSystem = new DeferredShadingSystem(mWindow->getViewport(0), mSceneMgr, mCamera);
298         SharedData::getSingleton().iSystem = mSystem;
299         mSystem->initialize();
300 
301         // safely setup application's (not postfilter!) shared data
302         SharedData::getSingleton().iCamera = mCamera;
303         SharedData::getSingleton().iRoot = mRoot;
304         SharedData::getSingleton().iWindow = mWindow;
305         SharedData::getSingleton().iActivate = true;
306         SharedData::getSingleton().iGlobalActivate = true;
307         SharedData::getSingleton().iMainLight = l1;
308 
309         //Create the scene
310         // Create "root" node
311         SceneNode* rootNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
312 
313         // Create the cathedral - this will be the static scene
314         Entity* cathedralEnt = mSceneMgr->createEntity("Cathedral", "sibenik.mesh");
315         SceneNode* cathedralNode = rootNode->createChildSceneNode();
316         cathedralNode->attachObject(cathedralEnt);
317 
318         createAtheneScene(rootNode);
319         createKnotScene(rootNode);
320         createObjects(rootNode);
321 
322         setupControls();
323     }
324 
createSampleLights()325     void createSampleLights()
326     {
327         // Create some lights
328         std::vector<Light*> lights;
329         SceneNode *parentNode = mSceneMgr->getRootSceneNode()->createChildSceneNode("LightsParent");
330         // Create light nodes
331         std::vector<Node*> nodes;
332 
333         Vector4 attParams = Vector4(4,1,0,7);
334         Real lightRadius = 25;
335 
336         Light *a = mSceneMgr->createLight();
337         SceneNode *an = parentNode->createChildSceneNode();
338         an->attachObject(a);
339         a->setAttenuation(attParams.x, attParams.y, attParams.z, attParams.w);
340         //a->setAttenuation(1.0f, 0.000f, 0.000f);
341         an->setPosition(0,0,lightRadius);
342         a->setDiffuseColour(1,0,0);
343         //a->setSpecularColour(0.5,0,0);
344         lights.push_back(a);
345         nodes.push_back(an);
346 
347         Light *b = mSceneMgr->createLight();
348         SceneNode *bn = parentNode->createChildSceneNode();
349         bn->attachObject(b);
350         b->setAttenuation(attParams.x, attParams.y, attParams.z, attParams.w);
351         bn->setPosition(lightRadius,0,0);
352         b->setDiffuseColour(1,1,0);
353         //b->setSpecularColour(0.5,0.5,0);
354         lights.push_back(b);
355         nodes.push_back(bn);
356 
357         Light *c = mSceneMgr->createLight();
358         SceneNode *cn = parentNode->createChildSceneNode();
359         cn->attachObject(c);
360         c->setAttenuation(attParams.x, attParams.y, attParams.z, attParams.w);
361         cn->setPosition(0,0,-lightRadius);
362         c->setDiffuseColour(0,1,1);
363         c->setSpecularColour(0.25,1.0,1.0); // Cyan light has specular component
364         lights.push_back(c);
365         nodes.push_back(cn);
366 
367         Light *d = mSceneMgr->createLight();
368         SceneNode *dn = parentNode->createChildSceneNode();
369         dn->attachObject(d);
370         d->setAttenuation(attParams.x, attParams.y, attParams.z, attParams.w);
371         dn->setPosition(-lightRadius,0,0);
372         d->setDiffuseColour(1,0,1);
373         d->setSpecularColour(0.0,0,0.0);
374         lights.push_back(d);
375         nodes.push_back(dn);
376 
377         Light *e = mSceneMgr->createLight();
378         SceneNode *en = parentNode->createChildSceneNode();
379         en->attachObject(e);
380         e->setAttenuation(attParams.x, attParams.y, attParams.z, attParams.w);
381         en->setPosition(lightRadius,0,lightRadius);
382         e->setDiffuseColour(0,0,1);
383         e->setSpecularColour(0,0,0);
384         lights.push_back(e);
385         nodes.push_back(en);
386 
387         Light *f = mSceneMgr->createLight();
388         SceneNode *fn = parentNode->createChildSceneNode();
389         fn->attachObject(f);
390         f->setAttenuation(attParams.x, attParams.y, attParams.z, attParams.w);
391         fn->setPosition(-lightRadius,0,-lightRadius);
392         f->setDiffuseColour(0,1,0);
393         f->setSpecularColour(0,0.0,0.0);
394         lights.push_back(f);
395         nodes.push_back(fn);
396 
397         // Create marker meshes to show user where the lights are
398         Entity *ent;
399         GeomUtils::createSphere("PointLightMesh", 0.05f, 5, 5, true, true);
400         for(std::vector<Light*>::iterator i=lights.begin(); i!=lights.end(); ++i)
401         {
402             Light* light = *i;
403             ent = mSceneMgr->createEntity(light->getName()+"v", "PointLightMesh");
404             String matname = light->getName()+"m";
405             // Create coloured material
406             MaterialPtr mat = MaterialManager::getSingleton().create(matname,
407                                                                      ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
408             Pass* pass = mat->getTechnique(0)->getPass(0);
409             pass->setDiffuse(0.0f,0.0f,0.0f,1.0f);
410             pass->setAmbient(0.0f,0.0f,0.0f);
411             pass->setSelfIllumination(light->getDiffuseColour());
412 
413             ent->setMaterialName(matname);
414             //ent->setRenderQueueGroup(light->getRenderQueueGroup());
415             ent->setRenderQueueGroup(DeferredShadingSystem::POST_GBUFFER_RENDER_QUEUE);
416             static_cast<SceneNode*>(light->getParentNode())->attachObject(ent);
417             ent->setVisible(true);
418         }
419 
420         // Store nodes for hiding/showing
421         SharedData::getSingleton().mLightNodes = nodes;
422 
423         // Do some animation for node a-f
424         // Generate helix structure
425         float seconds_per_station = 1.0f;
426         float r = 1.0;
427         //Vector3 base(0,-30,0);
428         Vector3 base(-8.75, 3.5, 0);
429 
430         float h=3;
431         const size_t s_to_top = 16;
432         const size_t stations = s_to_top*2-1;
433         float ascend = h/((float)s_to_top);
434         float stations_per_revolution = 3.5f;
435         size_t skip = 2; // stations between lights
436         Vector3 station_pos[stations];
437         for(size_t x=0; x<s_to_top; ++x)
438         {
439             float theta = ((float)x/stations_per_revolution)*2.0f*Math::PI;
440             station_pos[x] = base+Vector3(Math::Sin(theta)*r, ascend*x, Math::Cos(theta)*r);
441         }
442         for(size_t x=s_to_top; x<stations; ++x)
443         {
444             float theta = ((float)x/stations_per_revolution)*2.0f*Math::PI;
445             station_pos[x] = base+Vector3(Math::Sin(theta)*r, h-ascend*(x-s_to_top), Math::Cos(theta)*r);
446         }
447         // Create a track for the light swarm
448         Animation* anim = mSceneMgr->createAnimation("LightSwarmTrack", stations*seconds_per_station);
449         // Spline it for nice curves
450         anim->setInterpolationMode(Animation::IM_SPLINE);
451         for(unsigned int x=0; x<nodes.size(); ++x)
452         {
453             // Create a track to animate the camera's node
454             NodeAnimationTrack* track = anim->createNodeTrack(x, nodes[x]);
455             for(size_t y=0; y<=stations; ++y)
456             {
457                 // Setup keyframes
458                 TransformKeyFrame* key = track->createNodeKeyFrame(y*seconds_per_station); // A start position
459                 key->setTranslate(station_pos[(x*skip+y)%stations]);
460                 // Make sure size of light doesn't change
461                 key->setScale(nodes[x]->getScale());
462             }
463         }
464         // Create a new animation state to track this
465         auto animState = mSceneMgr->createAnimationState("LightSwarmTrack");
466         animState->setEnabled(true);
467 
468         auto& controllerMgr = ControllerManager::getSingleton();
469         controllerMgr.createFrameTimePassthroughController(AnimationStateControllerValue::create(animState, true));
470 
471         /*Light* spotLight = mSceneMgr->createLight("Spotlight1");
472          spotLight->setType(Light::LT_SPOTLIGHT);
473          spotLight->setAttenuation(200, 1.0f, 0, 0);
474          spotLight->setSpotlightRange(Degree(30.0), Degree(45.0), 0.8);
475          spotLight->setPosition(0,120,0);
476          spotLight->setDirection(0, -1, 0);
477          spotLight->setDiffuseColour(1,1,1);
478          spotLight->setSpecularColour(1,1,1);*/
479     }
480 };
481 
482 #endif
483