Help me understand the code of the standard shader

I’m currently trying to port some of my own code into the standard shader, but there’s a lot of it i don’t really understand. Maybe someone can help explain things to me. Just to make sure we’re on the same page, this is the source code i’m working from, which i’m reasonably sure is for the standard shader: Shader "Standard"{ Properties { [LM_Albedo] [LM_Transparency] _Color("C - Pastebin.com

[LM_Albedo] [LM_Transparency] _Color("Color", Color) = (1,1,1,1)   
        [LM_MasterTilingOffset] [LM_Albedo] _MainTex("Albedo", 2D) = "white" {}

hese lines, i understand to be declaring properties, a colour picker and an albedo texure. But what is this [LM_xxxx] stuff? there’s two of them at the start of each line, what do they mean?

What does This whole block of code mean?

CGINCLUDE
        //@TODO: should this be pulled into a shader_feature, to be able to turn it off?
        #define _GLOSSYENV 1
        #define UNITY_SETUP_BRDF_INPUT MetallicSetup
    ENDCG

Where is the albedo _MainTex used? I can’t find any reference to it in the code after its initial declaration?

I read on a uniy article that this shader is optimised so that features you don’t use, and don’t set a map for, aren’t processed or rendered hus saving resources. How does that actually work in practise?

I’m inserting some properties here

Properties
    {
        [LM_Albedo] [LM_Transparency] _Color("Color", Color) = (1,1,1,1)   
        [LM_MasterTilingOffset] [LM_Albedo] _MainTex("Albedo", 2D) = "white" {}
            _Mask ("Texture", 2D) = "white" {}
        _Color1 ("Channel 1 Colour", Color) = (1,1,1,1)
        _Color2 ("Channel 2 Colour", Color) = (1,1,1,1)
        _Color3 ("Channel 3 Colour", Color) = (1,1,1,1)
        _Color4 ("Channel 4 Colour", Color) = (1,1,1,1)

obviously these won’t function without more code later on, but as i undersandm the inputs for the mask texture, and four colour pickers, should appear in the inspector. but they don’t/.

  1. [LM_Albedo], [LM_Transparency] are attributes. Same as C# attributes like [Range(1, 2)], [Serializable], etc.
    I guess LM_Albedo means value of this property is used as albedo input for lightmaps generation.

  2. CGINCLUDE block just includes its content to the shader.
    It defines two preprocessor constants _GLOSSYENV and UNITY_SETUP_BRDF_INPUT

I don’t know what means first one but second one means use metallic workflow instead of specular.
In UnityStandardCore.cginc there is a line of code:

FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex);

When preprocessor replaces all macros values, it looks like:

FragmentCommonData o = MetallicSetup(i_tex);

So FragmentCommonData is the result of calling function MetallicSetup which is also defined in UnityStandardCore.cginc

  1. _MainTex is actually used Albedo function defined in UnityStandardInput.cginc.

Look for example at “FORWARD” pass.

#pragma vertex vertForwardBase
#pragma fragment fragForwardBase

 #include "UnityStandardCore.cginc"

It includes a cginc file which includes a lot more of cginc files and then uses functions vertForwardBase and fragForwardBase as vertex and fragment shaders. And somewhere deep in sources of that cginc files Albedo function gets called and it looks like this:

half3 Albedo(float4 texcoords)
{
    half3 albedo = _Color.rgb * tex2D (_MainTex, texcoords.xy).rgb;
#if _DETAIL
    #if (SHADER_TARGET < 30)
        // SM20: instruction count limitation
        // SM20: no detail mask
        half mask = 1; 
    #else
        half mask = DetailMask(texcoords.xy);
    #endif
    half3 detailAlbedo = tex2D (_DetailAlbedoMap, texcoords.zw).rgb;
    #if _DETAIL_MULX2
        albedo *= LerpWhiteTo (detailAlbedo * unity_ColorSpaceDouble.rgb, mask);
    #elif _DETAIL_MUL
        albedo *= LerpWhiteTo (detailAlbedo, mask);
    #elif _DETAIL_ADD
        albedo += detailAlbedo * mask;
    #elif _DETAIL_LERP
        albedo = lerp (albedo, detailAlbedo, mask);
    #endif
#endif
    return albedo;
}

So as you see it fetches from _MainTex as expected.

  1. Why do you not see your params.
    It is because Standard shader uses custom editor which is defined in Editor/StandardShaderGUI.cs file. You need to look at it and add code for your new params.

  2. How shaders are optimized when you don’t use something inside.
    It is done with shader keywords. Please look at
    Unity - Manual: Declaring and using shader keywords in HLSL

1 Like

i’ll freely admit i don’t understand most of that post, bu i will reread it and research until i do. the part about the custom editor is especially helpful <3

Short answer, those keywords have been deprecated in favor of the META pass and just haven’t been removed.

Long answer, Unity 4 and older had a very rigid system for baking lightmaps. Basically, your shader names had to have certain keywords and you had to use reserved property names in order for Beast to detect and read them. In an attempt to relax this requirement, the Unity 5 beta added attributes like these ones so that you could name properties whatever you wanted and then just tag them to have Enlighten detect them.

Late in the Unity 5 beta and into the release Unity dropped this approach in favor of a much more flexible system called the META pass. Here you can output a generic surface albedo and emission color from any source, including calculations within the shader. So as near as I can tell you can basically ignore those attributes now.

The Standard shader’s code is incredibly cryptic. It took me two months just to unscramble it when I was writing Alloy.

1 Like

this sounds bad. Has anyone tried to rewrite it and fix these issues?

@Nanako
Well like I said, they’re benign. The most harm they could do now is causing confusion for people who are digging around inside the Standard shader.

ok, at the moment i’m thinking it might be simplest to do the stuff i want to do on the mainTex before it gets passed to the cginc things. But where might i do that?

I’m not entirely clear on what a cginc is, and i’m not sure i want to go digging in them. I certainly don’t want to change the way the standard shader works, is it possible to duplicate and use my own cgincs ? would i need to copy them all?

i need to do some operations on the main texture, i’ve already got it working in a simpler shader, but with that i just took the mainTex and did my stuff to create the output. I can’t really tell if the maintex is hooked in anywhere here.

@Nanako
A “.cginc” file is a header of shareable shader code. For Unity’s shaders, the “#include” directive works two ways:

  • You specify the file name, and it will look first in the same directory as the shader file, and second in Unity’s global header directory.
  • You specify a path with the root being the “Assets” directory, and the header and shader file can be anywhere in the project.

By using the first behavior, it is possible to copy the standard shader into a folder in your project and then copy one of Unity’s provided headers into the same folder. You can then change the contents of the header and the shader will use it to override the one that Unity provides.

So to modify the the Albedo inputs, you would want to modify “UnityStandardInput.cginc”. For examples how all of this works, check out the customized shaders in this thread:
Using GGX instead of Blinn-Phong

HII @
thank you, i’ve found the file and am modifying it now. However, a problem concerns me. Since i’m replacing the previous standardInput.cginc with a modified local copy, it’s going to be used by everything whicih would normally use that, including the original standard shader (which i’d still like to use alongside my new/modified version)

To that end, it seems like i’d need some way of which shader is calling the code. How might i do that?

Can i just check for the existence of one of my custom variables? (_Mask) or would that throw an exception for shaders which don’t have it?

@Nanako
No no, it is only used by shaders in the same folder. The global Standard shader will be unaffected.

huzzah! i made it all work. Thank you very much <3

1 Like