- Home /
Switching shader Lighting On/Off from script
Hello everyone!
I'm trying to write a script that would be able to tell a shader whether it should use lighting or not.
I am familiar with possibility to add Lighting On/Off
before CGPROGRAM
line (as addressed here: http://answers.unity3d.com/questions/234433/shader-light-off-in-cgprogram.html ) but I was wondering if it was possible to toggle it from MonoBehaviour
?
I tried adding preprocessor directives like
SubShader
{
Tags { "RenderType" = "Opaque" }
#if LIGHTING_ON
Lighting On
#else
Lighting Off
CGPROGRAM
#pragma surface surf Lambert
#pragma multi_compile LIGHTING_OFF LIGHTING_ON
// ...
}
and toggle lighting with material.EnableKeyword
but it throws me syntax error. I guess I can't use preprocessor directives before CGPROGRAM
line?
Is there something like material.shader.SetLighting(true)
? Should I write two different shaders, one with Lighting On
and one with Lighting Off
and switch between them or is there a better way?
I'm new to shaders so I hope I'm not missing something obvious. Any help is appreciated. Thank you!
EDIT 1: I don't want to do my lighting calculations. I would like to use surface shader with built-in Lambert shading like in my example.
EDIT 2: As @Jessespike suggested, it would be possible to copy-paste Unitys implementation of Lambert lighting model and have it wrapped inside #if LIGHTING_ON - #else
preprocessor directives, but would I still be able to use baked lightmaps?
Answer by Jessespike · Apr 24, 2015 at 03:29 PM
You could write your own property called _Lighting and have that as a condition to do lighting calculations.
In script:
material.SetFloat("_Lighting", 0f); // turn off;
material.SetFloat("_Lighting", 1f); // turn on;
In shader:
if (_Lighting != 0f)
{
// do light calcuations
}
Thank you for your answer but I think that would be quite inefficient. As I understand, if-else statements are costly in shaders and should be avoided.
Also, if I'm going to make my own lighting calculations, I could wrap that code in preprocessor if-else directives and have it multi compiled. That is actually how I do it now, but I don't want my lighting calculations. I want to use Unitys. I guess I wasn't so clear about that so I'll edit my question.
Then swapping between two shaders might be the way to go.
Not sure of anything like: material.shader.SetLighting(true)
Bit off track, I understand you don't want to write a custom lighting model, but just as an alternative, you could copy Unity's lambert function and wrap the lighting in pragmas.
#if LIGHTING_ON
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
#else
c.rgb = s.Albedo;
#endif
then in a script you can toggle with keywords:
public $$anonymous$$aterial material;
bool useLighting = false;
// Update is called once per frame
void OnGUI () {
if (GUI.Button(new Rect(0f, 0f, 100f, 100f), "Toggle Lighting"))
{
useLighting = !useLighting;
if (useLighting)
{
material.Disable$$anonymous$$eyword("LIGHTING_OFF");
material.Enable$$anonymous$$eyword("LIGHTING_ON");
} else {
material.Disable$$anonymous$$eyword("LIGHTING_ON");
material.Enable$$anonymous$$eyword("LIGHTING_OFF");
}
}
As a general rule, you can expect an if statement in a shader to always take as long as the longer condition. If the "else" takes three times as long to calculate as the "if" portion, the "if" would still take as long as the "else" would.
@Jessespike That is exactly what I'm doing right now :) , but I think that would leave me without option of baking light. As far as I'm aware, Unity uses lightmaps inside its implementation of lighting models (such as Lambert). I think I'd rather have 2 shaders than copy/paste whole implementation of lighting with lightmaps and everything. (That is if Unity really does use lightmaps inside lighting implementation. Not sure about that) I think I'll edit my question once more. Thanks.
@$$anonymous$$o $$anonymous$$haon Can you provide a link for further reading on this "if-else" topic? I was of opinion that both "if" and "else" are both executed and then one of those discarded. Since vertex/pixel shaders run for all vertices/pixels in parallel, I thought that every vertex/pixel has to run the same code. If one pixel satisfies "if" condition, and one pixel doesn't, both "if" and "else" have to be executed. Don't know what if none of them satisfy "if" condition. Can you provide more info?
Your answer
Follow this Question
Related Questions
Surface Shaders have Slight Trasparency. Why? 0 Answers
Converting Fixed Function Shader to Surface Shader 2 Answers
[ShaderGraph] How can I prevent lights overlapping in intensity? 0 Answers
How do I keep an object lit when a light moves past it? 0 Answers
Semitransparent shader not visible in front of objects 1 Answer