Saturday, October 12, 2013

Framework Update : Lighting

           Wow! It has been a quite sometime! Things have been very busy with me lately which hardly leaves any spare time to work on something extra outside of my job. Out of all time I could gather, I have made some major changes to the framework to accommodate easy to tweak features. Out of all the changes, one of the major change was the addition of Interface class IObject, which is mother of all objects in the scene.


             
             So the way this works is, GameObject class represents every object in the scene deriving from IObject interface. Every Game Object will have set of common properties, like, old properties, Position, Orientation, Scale, unique ID & of course, unique name. GameObject class only handles management of these parameters for an object. From GameObject, derives MeshObject class. As the name suggests, this class is responsible for loading, rendering & updating of Model which is our custom implementation for loading mesh format using Assimp. This class also handles, message passing of model's transformation in the scene, it's material properties, & the shader used for actual rendering. This class uses my earlier written, AssimpLoader class for loading. 

             Now comes the main part for which I had to change lot of stuff inside the framework, Lighting. So far, all my shaders were using, single direction vector to emulate directional light shader. Of course, to make things more interactive, I decided to add even multiple point lights inside the framework. After reading a lot about limitation of forward renderer with number of lights it supports etc etc, I read bit about how to go about writing deferred renderer. Things look quite interesting at that part of the sea, but I felt, doing first the basics will give me decent idea & also expose me to some of the limitations of forward renderer. So without any delay, all I had to was add custom light classes, namely, DirectionalLightObject & PointLightObject deriving from, ofcourse GameObject. Immediately, I had access to this objects transformations.  

Here are some outputs with new lighting :

Red light in action, green omni was kept with less range...


This triggered another change, in my level parser class, which somehow now had to decide, which type of object it is that it's trying to load? If it's a MeshObject then do something, If it's a LightObject then based on Point Light or Directional Light, assign parameters to that derived GameObject. Obviously, things are bit messy in my level file right now, and slowly am realizing that I even need to re-write that module as well for better code maintenance. Perhaps XML format to store the scene level data? As of now, I have chosen to keep my level file format intact with few additions to it. Every addition leads to me changing LevelParser code as well, which doesn't seem like good design from my side. Need to come up with generic approach to this. This has been added to my TO DO list. Here is how my Level.scn file looks at the moment :


    MeshBegin
        ObjectId [2]
        ObjectName [IronMan]
        ObjectType [StaticMesh]
        ObjectPath [Data\\ASSIMP\\COLLADA\\ironman.DAE]       
        ObjectShader [CookTorrance]
        ObjectPosition [150.0f] [250.0f] [100.0f]
        ObjectRotation [1.0f] [0.0f] [0.0f] [90.0f]
        ObjectScale [1.0f] [1.0f] [1.0f]
        ObjectVisibility [1]
        EmissiveColor [0.9] [0.9] [0.9] [1.0]
        AmbientColor [0.6] [0.6] [0.6] [1.0]
        DiffuseColor [0.8] [0.8] [0.8] [1.0]
        SpecularColor [0.5] [0.5] [0.5] [1.0]
    MeshEnd
    MeshBegin
        ObjectId [3]
        ObjectName [SpiritBreaker]
        ObjectType [StaticMesh]
        ObjectPath [Data\\ASSIMP\\COLLADA\\SpiritBreaker.dae]       
        ObjectShader [Diffuse]
        ObjectPosition [0.0f] [250.0f] [300.0f]
        ObjectRotation [0.0f] [1.0f] [0.0f] [0.0f]
        ObjectScale [1.0f] [1.0f] [1.0f]
        ObjectVisibility [1]
        EmissiveColor [0.8] [0.8] [0.8] [1.0]
        AmbientColor [0.6] [0.6] [0.6] [1.0]
        DiffuseColor [0.8] [0.8] [0.8] [1.0]
        SpecularColor [0.5] [0.5] [0.5] [1.0]
    MeshEnd
 And for lights : 

         LightBegin
             ObjectType [PointLight]
             ObjectId [6]
             ObjectName [YellowOmni]
             LightColor [1.0f] [1.0f] [0.0f] [1.0f]
             LightPosition [100.0f] [400.0f] [100.0f]
             LightIntensity [5.0f]
             LightRadius [100.0f]
         LightAttenuation [0]
         LightEnd
         LightBegin
             ObjectType [PointLight]
             ObjectId [7]
             ObjectName [GreenOmni]
             LightColor [0.0f] [1.0f] [0.0f] [1.0f]
             LightPosition [50.0f] [400.0f] [200.0f]
             LightIntensity [2.0f]
             LightRadius [50.0f]
             LightAttenuation [0]
         LightEnd


And so on ...

Say Cheeeeeeeeessssseee!!

I wrote a small utility class LightsManager, whose sole task is to keep entry of all the Light objects in the scene separately during scene loading. This is very useful while actual rendering. During each render, I traverse through the list of Light Objects in the scene via LightManager class, get the property of each light & set the shader parameters accordingly. Easier said than done!  I had to also change the shader code to traverse through list of lights in the scene & do the shading calculations accordingly. Now I can see, as I keep on adding more lights into the scene, this list traversal becomes hell of a task, also, inside shader, more the lights, the performance - frame rate, takes its toll. Right now, my framework can happily render 4-6 point lights & upto 4-6 directional lights at the same time. But yes, this is some limitations. Though in reality, I can always optimize this calculations, based on number of lights actually affecting the object etc etc, but that is for later. This is where I somehow convinced that Deferred rendering is the way to go if I really need huge number of lights in the scene. 

 
Being avid fan of Dota2 ( steam id : dod1530 ) & introduction by one my colleague to Steam Workshop, has given me opportunity to use one of my favorite hero SpiritBreaker as a rendering character itself in my framework. Right now, am still getting hang of MD5Mesh format exporter which is going to be the final mesh format for the framework, hence all the models rendered now in screenshot are Collada mesh format.


Am actually not aware if I can use those models in my framework or not, so if anyone has any information on this regards, please let me know, I will remove them immediately. Of course, this framework is for my own learning & there is no financial gain for me, so it should be ok I guess.  

Here is one final output with all three omni lights on.


All Three lights in action, good to see nice blending of colors on the mesh!
Please let me know in case anyone has better way of doing same thing or simply wants to chit-chat on above topic. 

Cheers,
DoD.

         

No comments: