The first thing you need to decide is where this "enableGlow" parameter will live. I'll assume it will live in HlmsPbsDatablock. Let's call it bool mHasGlow.
Be careful not to put the variable in the middle of this declaration:
Code: Select all
float mkDr, mkDg, mkDb; //kD
float mkSr, mkSg, mkSb; //kS
float mFresnelR, mFresnelG, mFresnelB; //F0
Because we memcpy this data directly to the GPU in HlmsPbsDatablock::uploadToConstBuffer (unless you want the Vertex/Pixel shader to access this data, in which case you'll have to modify the template's material structure to keep the offsets correct).
Now, once you have the boolean flag, you can read it in HlmsPbs::calculateHashForPreCreate
Code: Select all
setProperty( "glow", datablock->mHasGlow );
You will notice in the code that instead of writing "glow" directly, I use global constant variables. That is because I want to force the compiler to calculate the hash of string at compile time (or worst case at launch time) instead of hashing the string every time this code is executed (it will depend on how good the compiler optimizer is... let's say I don't trust it...). In other words it's just an optimization.
By doing this, @property( glow ) will now work.
Then the function HlmsPbs::calculateHashForPreCaster
will delete this property since it's not needed for shadow casting. If you actually need it, you will have to modify its logic to spare the property variable from being deleted.
Deleting unused properties for the shadow casters is important because having the property "glow" set to 1, set to 0, and as non-existant property will be seen as 3 different materials by the RenderQueue (even if the shader template ignores the glow when shadow casting is set); which means less optimization opportunities when sorting ther RQ (batching) and possibly more memory used (which isn't much on its own, but it starts to add up with many datablocks and many different feature combinations).
Last but not least; when you toggle HlmsPbsDatablock::mHasGlow; the change won't be seen automatically by all the Renderables currently using your datablock. In order to do that, you need to call Hlms::flushRenderables
See HlmsPbsDatablock::setDetailMapBlendMode for an example.
You need to call Hlms::flushRenderables whenever a new shader code is needed; because the actual code is modified.
Is flushRenderables cheap? Not really. But it's not super expensive either. It depends:
flushRenderables will iterate over all Renderables currently using that datablock. If this datablock is used by 1.000.000 renderables, it will make 1.000.000 iterations to reevaluate the Renderables again. Calling this frequently would be counterproductive.
Also the first few times you will end up probably generating new shader code, which needs to be compiled. Once it has been compiled for the first time, Ogre will save a cache; so toggling the setting back to its original value (ceteris paribus) won't trigger another recompilation.
If you need to toggle waaaay too often; then it may be best to use the glow code for all objects, and disable it via the pixel shader by reading the material variables in the GPU (i.e. multiply by 0 when glow is disabled).
Do not confuse flushRenderables with with HlmsPbsDatablock::scheduleConstBufferUpdate
: This is only needed when you change a material variable that lives in the GPU buffer; like the diffuse colour (see HlmsPbsDatablock::setDiffuse). As the name implies this function schedules the datablock to later eventually uploadToConstBuffer call (and upload all
variables, not just the ones that changed, in burst across the PCI-e bus).
Some functions may have to do both (flushRenderables and scheduleConstBufferUpdate); such as HlmsPbsDatablock::setFresnel: If the fresnel value changes, we need to update the GPU data. But if also the fresnel changes from being the same value for all 3 channels (RGB) to being different for each channel, different shader code is needed and thus we call flushRenderable.