I have a shader that needs to be used with both backface culling on and off in separate occasions. Is there a way to edit the parameter passed with the Cull keyword (‘Back’, ‘Off’) in the material editor window for the shader?
Pass {
//Need to modify the following prameter(s?) through the material editor:
Cull Off
ZWrite On
Blend SrcAlpha One
ZTest LEqual
ColorMask RGBA
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
//shader code
//custom material editors area of effect?
ENDCG
}
I tried experimenting with custom material editors but it looks like they can only be used within the #CGPROGRAM#ENDCG block and the Cull option is defined outside of it.
PS: Creating a different shader just for this feature is not an option
We currently have a roster of shaders previously created and they are all waiting in the pipeline to be optimized, so we don’t want to increase the backlog by adding more shaders as of now. I know, it’s a lame, political excuse but I can push for adding a new shader if that is the best way of doing it.
If all I really need to do is change the culling option in the shader and nothing else, is adding a new shader the most elegant option? Also, How much does adding new shaders affect the performance of the game?
Short of actually duplicating/flipping the geometry, that’s about the only thing you can do.
But once you’ve optimised the shader you want to be double sided, can’t you just copy/paste it and then edit the line that says Cull?
Or you could potentially set up some LOD levels for subshaders and toggle the shader LOD level, assuming you’re not using that for anything else? (Personally, I’d just duplicate the geometry, or add a new shader).
I did think of this earlier, but we already have a lot of assets in game using the shader with Cull Off and we don’t have enough time to go back and fix those.
Anyhow, new shader it is, thanks for clearing things out all!
Came across this post when searching for a solution. Here is the solution for others. Add the cull to your properties and change your cull line to use the variable.
This same method works for blend modes, z-write etc.
How much I hate when you want to do something and everybody keeps telling you “why would you want to do that?” instead of helping… THANK YOU VERY VERY MUCH!
@glitchers_1 Thanks! You saved me a lot of time and energy on this. I found the API info on CullMode but there is no explanation on how to use it. (Unity - Scripting API: CullMode)
For anyone else looking into this, you can also change the Cull mode at runtime.
To demonstrate I used the example above to add a parameter to the shader and then in script created a togglable bool that changes between backface culling and double-sided.
public void SetShaderBackfaces(bool isBackfaced)
{
if (isBackfaced)
{
if (renderers != null)
{
foreach (Renderer rend in renderers)
{
rend.material.SetFloat("_Cull", 2);
}
}
}
else
{
if (renderers != null)
{
foreach (Renderer rend in renderers)
{
rend.material.SetFloat("_Cull", 0);
}
}
}
}
The important line to note is this:
rend.material.SetFloat("_Cull", 0);
The other bits just make sure that I have a referenced renderer before I start making changes.
Any render state you can set with a material property (which is basically all of them) you can change at runtime.
The only caveat is you actually have to change the material’s properties from the inspector or a custom script. You cannot modify them with a material property block or Unity animation (which internally uses a material property block).
Man… I wish I had seen this a few months ago! It would have saved me so much time when I was doing my ubershader… I had to learn it the hard way
All of this is kinda undocumented.
It is actually documented. It’s just Unity’s Shader documentation is sprawling, disjointed, and often not terribly in depth for how much of it there is.
Of course, that second quote is, while not technically wrong, very confusing. You can only specify up to 7 name / value pairs in the shader, but you can use enums with any number of elements. It was extra confusing because the Unity documentation for some of the enums were either missing, or if they were included in the documentation were not in the order they are in the actual enum. That made trying to add enums with a limited number of options from the longer list required a lot of guess and check. That’s luckily been fixed by now.
Yeah for me, I found and read in the docs about being able to use the property names in square brackets but I was under the mistaken impression that I couldn’t use a float value in place of the Cull values.
I saw the valid values in the docs as: Off, Back, and Front…and I didn’t realize that internally those values are simply replaced with float values by the compiler (I should’ve known – ). Also, I didn’t have the confidence that the numeric representations of Off, Back, and Front mapped exactly to the UnityEngine.Rendering.CullMode enum values.
Either way, many thanks to @glitchers_1 for the revelation.
To tack on to @ASGS_DumbFox 's comment, I’ll just point out that you can cast the enum value to an int when you go to set it on the material or material property block like so:
Also remember good practice is to cache the shader property to an int and use that instead of a string. I created a ShaderProperty static class that sets these up in its static constructor like so:
public static class ShaderProperty {
public static readonly int Color;
public static readonly int MainTex;
public static readonly int Smoothness;
public static readonly int Metallic;
public static readonly int EmissionTex;
public static readonly int EmissionTint;
public static readonly int EmissionIntensity;
public static readonly int Cull;
static ShaderProperty() {
Color = Shader.PropertyToID("_Color");
MainTex = Shader.PropertyToID("_MainTex");
Smoothness = Shader.PropertyToID("_Smoothness");
Metallic = Shader.PropertyToID("_Metallic");
EmissionTex = Shader.PropertyToID("_EmissionTex");
EmissionTint = Shader.PropertyToID("_EmissionTint");
EmissionIntensity = Shader.PropertyToID("_EmissionIntensity");
Cull = Shader.PropertyToID("_Cull");
}
}
Now I can use those properties in code instead of the string values (also nice for readability and always knowing the correct property key instead of accidentally typing the incorrect key name as text, etc.
using UnityEngine.Rendering;
materialPropertyBlock.SetInteger(ShaderProperty.Cull, (int)CullMode.Front);
Is Cull a property that can be modified through a material property block in code? I’ve tried setting a cull property through a material property block but it doesn’t seem to be having an effect. Setting the cull property through code by accessing the Renderer’s material’s SetFloat works on the other hand.
You can fake it using VFACE and discard to find out which side of the triangle you’re looking at and not render it, though this is not as cheap as using Cull.
Thanks for the reply. Is there a reason behind this? Or any docs I can read to learn more about which properties can/cannot be set by material property blocks?
Anything inside of the CGPROGRAM or HLSLPROGRAM block of code can be modified by the property blocks. Anything outside of that cannot be.
The reason why is property blocks only affect the data passed to GPU for the shader passes on the GPU, where as the stuff outside of those blocks, things like Cull or Blend, etc. are setting the render state for the draw call. There is a cost, both on the CPU and GPU, to render state changes, so material property blocks were explicitly designed to not affect those, presumably so the internal code knows it can apply them safely without needing to worry about that overhead.
Though it should be noted that if you’re using the URP or HDRP you shouldn’t use property blocks as they’ve heavily optimized for unique materials in the SRP and property blocks are actually slower that directly modifying the materials.