Hey there!
Requested functionality:
We should be able to set a list of shader keywords (Preferably NonAlloc API) on the renderer instead of on individual materials. Something similar to the MaterialPropertyBlock API but for keywords. It would be good to be able to add keywords that we want enabled, and keywords we want to disable, and the contents of this ‘MaterialKeywordBlock’ will ‘merge in’ with existing keywords and stomp over keywords that were redefined - the same way MaterialPropertyBlock works. Usage could mirror the MaterialPropertyBlock API.
Some mock examples:
Basic usage:
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
mpb.SetColor("_MyColor",Color.white);
myRenderer.SetPropertyBlock(mpb);
MaterialKeywordBlock mkb = new MaterialKeywordBlock();
mkb.EnableKeyword("SOME_KEYWORD");
myRenderer.SetKeywordBlock(mkb);
Get existing keywords, to remain compatible with other scripts that might also want to set the property/keyword blocks:
MaterialPropertyBlock mpb = new MaterialPropertyBlock();
myRenderer.GetPropertyBlock(mpb);
mpb.SetTexture("_Texture", someTexture);
myRenderer.SetPropertyBlock(mpb);
MaterialKeywordBlock mkb = new MaterialKeywordBlock();
myRenderer.GetKeywordBlock(mkb);
mkb.EnableKeyword("SOME_KEYWORD");
myRenderer.SetKeywordBlock(mkb);
Existing Solutions and their problems:
Iterate over sharedMaterails, and set keywords directly
Similar to setting a property directly on the material, this ‘dirties’ the material asset. It means that the changes you make in the editor will persist when you stop playing the game, potentially corrupting project data, and it will also mean that every renderer using the material will be effected by the keyword changes.
On Awake, create an instance of sharedMaterial, and manage the keywords on that
There is multiple issues with this. Firstly, it ruins the editor workflow. The designer would like to, at any time, change the material on the renderer. If you are managing an instance of this material via your code, you will need to write code to detect when the user changed the material reference, so you can destroy your old material and instantiate a copy of the new material that the designer has selected. Its a mess. Secondly, it means we need to manage the lifecycle of the instantiated material - which can get fairly dangerous if you want your script to execute during edit mode. For example, if you do not revert the shared material correctly when the user is editing a prefab with your script on it, then the prefab would save to disk with your material instance and it’ll loose the reference to its original material. Thirdly, it means that different scripts will not be able to coexist as easily, as they will all be attempting to manage their own instance of the material. The ‘last’ script to execute will be the only one that works.
How the proposed feature fixes the issue:
MaterialPropertyBlocks make it easy to create and drive complex effects on any renderer in the project by removing the need to micro manage materials and providing API’s for different scripts to coexist (Renderer.GetProperyBlock). The proposed feature will bring the same feature set to shader keywords.