How to add slider for normal power on a surface shader?

How do you increase the normal strength of a bump map?

Shader "Custom/Planet" {
    Properties {
     
        _Tex1 ("Texture 0 (Beach)", 2D) = "white" {}
        _Color1 ("Color 0", Color) = (1,1,1,1)
        _Nor1 ("Normal 0", 2D) = "bump" {}
       
/////trying to add this slider for the normal strength
        _NormalPower1 ("Normal Power", Range(0,4)) = 0.5

        _Glossiness1 ("Smoothness", Range(0,1)) = 0.5
        _Metallic1 ("Metallic", Range(0,1)) = 0.0                 
        _TexScale1 ("Texture Scale 0", Float) = 1

        _Tex2 ("Texture 1", 2D) = "white" {}
        _Color2 ("Color 1", Color) = (1,1,1,1)
        _Nor2 ("Normal 1", 2D) = "bump" {}
        _Glossiness2 ("Smoothness", Range(0,1)) = 0.5
        _Metallic2 ("Metallic", Range(0,1)) = 0.0
        _TexScale2 ("Texture Scale 1", Float) = 1

        _Tex3 ("Texture 2", 2D) = "white" {}
        _Color3 ("Color 2", Color) = (1,1,1,1)
        _Nor3 ("Normal 2", 2D) = "bump" {}
        _Glossiness3 ("Smoothness", Range(0,1)) = 0.5
        _Metallic3 ("Metallic", Range(0,1)) = 0.0
        _TexScale3 ("Texture Scale 2", Float) = 1

        _Tex4 ("Texture 3", 2D) = "white" {}
        _Color4 ("Color 3", Color) = (1,1,1,1)
        _Nor4 ("Normal 3", 2D) = "bump" {}
        _Glossiness4 ("Smoothness", Range(0,1)) = 0.5
        _Metallic4 ("Metallic", Range(0,1)) = 0.0
        _TexScale4 ("Texture Scale 3", Float) = 1

        _Tex5 ("Texture 4", 2D) = "white" {}
        _Color5 ("Color 4", Color) = (1,1,1,1)
        _Nor5 ("Normal 4", 2D) = "bump" {}
        _Glossiness5 ("Smoothness", Range(0,1)) = 0.5
        _Metallic5 ("Metallic", Range(0,1)) = 0.0
        _TexScale5 ("Texture Scale 4", Float) = 1

        _Tex6 ("Texture 5", 2D) = "white" {}
        _Color6 ("Color 5", Color) = (1,1,1,1)
        _Nor6 ("Normal 5", 2D) = "bump" {}
        _Glossiness6 ("Smoothness", Range(0,1)) = 0.5
        _Metallic6 ("Metallic", Range(0,1)) = 0.0
        _TexScale6 ("Texture Scale 5", Float) = 1

     

    }
    SubShader{

        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0


        sampler2D _Tex1;
        sampler2D _Tex2;
        sampler2D _Tex3;
        sampler2D _Tex4;
        sampler2D _Tex5;
        sampler2D _Tex6;

        sampler2D _Nor1;
        sampler2D _Nor2;
        sampler2D _Nor3;
        sampler2D _Nor4;
        sampler2D _Nor5;
        sampler2D _Nor6;

     

        float _TexScale1;
        float _TexScale2;
        float _TexScale3;
        float _TexScale4;
        float _TexScale5;
        float _TexScale6;
 
     
     

        struct Input {
            float2 uv_Tex1;
            float2 uv4_Tex2;     
            float4 color: Color;
        };

        half _Glossiness1;
        half _NormalPower1;  /////- this is one method I have tried
        half _Metallic1;
        fixed4 _Color1;

        half _Glossiness2;
        half _Metallic2;
        fixed4 _Color2;

        half _Glossiness3;
        half _Metallic3;
        fixed4 _Color3;

        half _Glossiness4;
        half _Metallic4;
        fixed4 _Color4;

        half _Glossiness5;
        half _Metallic5;
        fixed4 _Color5;

        half _Glossiness6;
        half _Metallic6;
        fixed4 _Color6;

 

     

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o) {

            fixed4 c1 = tex2D (_Tex1, IN.uv_Tex1 * _TexScale1) * _Color1;
            fixed4 c2 = tex2D (_Tex2, IN.uv_Tex1 * _TexScale2) * _Color2;
            fixed4 c3 = tex2D (_Tex3, IN.uv_Tex1 * _TexScale3) * _Color3;
            fixed4 c4 = tex2D (_Tex4, IN.uv_Tex1 * _TexScale4) * _Color4;
            fixed4 c5 = tex2D (_Tex5, IN.uv_Tex1 * _TexScale5) * _Color5;
            fixed4 c6 = tex2D (_Tex6, IN.uv_Tex1 * _TexScale6) * _Color6;
         
         

         
            //half slopeIntensity = 1 - (IN.color.r + IN.color.g + IN.color.b + IN.color.a + IN.uv4_Tex2.x + IN.uv4_Tex2.y);

            o.Albedo = IN.color.r * c1 + IN.color.g * c2 + IN.color.b * c3 + IN.color.a * c4 + IN.uv4_Tex2.x * c5 + IN.uv4_Tex2.y * c6;
            o.Metallic = IN.color.r * _Metallic1 + IN.color.g * _Metallic2 + IN.color.b * _Metallic3 + IN.color.a * _Metallic4 + IN.uv4_Tex2.x * _Metallic5 + IN.uv4_Tex2.y * _Metallic6;
            o.Smoothness = IN.color.r * _Glossiness1 + IN.color.g * _Glossiness2 + IN.color.b * _Glossiness3 + IN.color.a * _Glossiness4 + IN.uv4_Tex2.x * _Glossiness5 + IN.uv4_Tex2.y * _Glossiness6;
         
/////one method I have tried, when I increase the normal power to maximum, the terrain goes dark
            o.Normal =  UnpackNormal(tex2D(_Nor1, IN.uv_Tex1 * _TexScale1) * _NormalPower1) * IN.color.r + UnpackNormal(tex2D(_Nor2, IN.uv_Tex1 * _TexScale2)) * IN.color.g
            + UnpackNormal(tex2D(_Nor3, IN.uv_Tex1 * _TexScale3)) * IN.color.b + UnpackNormal(tex2D(_Nor4, IN.uv_Tex1 * _TexScale4)) * IN.color.a
            + UnpackNormal(tex2D(_Nor5, IN.uv_Tex1 * _TexScale5)) * IN.uv4_Tex2.x + UnpackNormal(tex2D(_Nor6, IN.uv_Tex1 * _TexScale6)) * IN.uv4_Tex2.y;
        }
        ENDCG
        }
     
 
    FallBack "Diffuse"
}

As you giving the custom shader, I will try to describe general concept behind normal. Well the idea of normal map is to create micro details to surface. Normal map only describes the direction of this in-regularities of surface, so first of all you have to decide what this “normal strength” represent in real world (I’m guessing you want something close to physical based rendering), for this there is few options:

  • Create you own imaginary function, etc. scale light power (Outside of PBR).
  • Interpolate between surface normal vector and normal map vector.
  • Scale the vector by magic value and re-normalize.
  • Use parallax technique (https://en.wikipedia.org/wiki/Parallax_mapping)

Also I really recommend you just creating from template surface shader and generating it to see how we implement all kinds of shading techniques. Also you can download our built-in shaders for each version (https://unity3d.com/get-unity/download/archive), by clicking on Downloads and selecting in drop down “Built in shaders”.

Hey LukasCH thanks for the reply, I don’t know if i did it correctly but this seemed to work:

I just created a float for normal power and multiplied that by the tex2D.

I am not sure if I did it correctly though, but it does add a little bit more depth but it might be off?

o.Normal = UnpackNormal(tex2D(_Nor1, IN.uv_Tex1 * _TexScale1) * _NorPow1)

void surf (Input IN, inout SurfaceOutputStandard o) {

            fixed4 c1 = tex2D (_Tex1, IN.uv_Tex1 * _TexScale1) * _Color1 * _Brightness1;
            fixed4 c2 = tex2D (_Tex2, IN.uv_Tex1 * _TexScale2) * _Color2 * _Brightness2;
            fixed4 c3 = tex2D (_Tex3, IN.uv_Tex1 * _TexScale3) * _Color3 * _Brightness3;
            fixed4 c4 = tex2D (_Tex4, IN.uv_Tex1 * _TexScale4) * _Color4 * _Brightness4;
            fixed4 c5 = tex2D (_Tex5, IN.uv_Tex1 * _TexScale5) * _Color5 * _Brightness5;
            fixed4 c6 = tex2D (_Tex6, IN.uv_Tex1 * _TexScale6) * _Color6 * _Brightness6;
           
            fixed4 ao1 = tex2D (_AOTex1, IN.uv_Tex1 * _TexScale1);
            fixed4 ao2 = tex2D (_AOTex2, IN.uv_Tex1 * _TexScale1);
            fixed4 ao3 = tex2D (_AOTex3, IN.uv_Tex1 * _TexScale1);
            fixed4 ao4 = tex2D (_AOTex4, IN.uv_Tex1 * _TexScale1);
            fixed4 ao5 = tex2D (_AOTex5, IN.uv_Tex1 * _TexScale1);
            fixed4 ao6 = tex2D (_AOTex6, IN.uv_Tex1 * _TexScale1);
           
           

            o.Alpha = ao3.a;
        //    o.Occlusion = IN.color.r * ao1 + IN.color.g * ao2 + IN.color.b * ao3 + IN.color.a * ao4 + IN.uv4_Tex2.x * ao5 + IN.uv4_Tex2.y * ao6;
           
            //half slopeIntensity = 1 - (IN.color.r + IN.color.g + IN.color.b + IN.color.a + IN.uv4_Tex2.x + IN.uv4_Tex2.y);

            o.Albedo = IN.color.r * c1 + IN.color.g * c2 + IN.color.b * c3 + IN.color.a * c4 + IN.uv4_Tex2.x * c5 + IN.uv4_Tex2.y * c6;
            o.Metallic = IN.color.r * _Metallic1 + IN.color.g * _Metallic2 + IN.color.b * _Metallic3 + IN.color.a * _Metallic4 + IN.uv4_Tex2.x * _Metallic5 + IN.uv4_Tex2.y * _Metallic6;
            o.Smoothness = IN.color.r * _Glossiness1 + IN.color.g * _Glossiness2 + IN.color.b * _Glossiness3 + IN.color.a * _Glossiness4 + IN.uv4_Tex2.x * _Glossiness5 + IN.uv4_Tex2.y * _Glossiness6;
           
            o.Normal = UnpackNormal(tex2D(_Nor1, IN.uv_Tex1 * _TexScale1) * _NorPow1) * IN.color.r + UnpackNormal(tex2D(_Nor2, IN.uv_Tex1 * _TexScale2) * _NorPow2) * IN.color.g
            + UnpackNormal(tex2D(_Nor3, IN.uv_Tex1 * _TexScale3) * _NorPow3) * IN.color.b + UnpackNormal(tex2D(_Nor4, IN.uv_Tex1 * _TexScale4) * _NorPow4) * IN.color.a
            + UnpackNormal(tex2D(_Nor5, IN.uv_Tex1 * _TexScale5) * _NorPow5) * IN.uv4_Tex2.x + UnpackNormal(tex2D(_Nor6, IN.uv_Tex1 * _TexScale6) * _NorPow6) * IN.uv4_Tex2.y;
        }

This will certain do something to the normals, but it’s not quite right. Using your code will scale the normals, but also skew them to the side. You should use the built in function for scaling normals:

UnpackScaleNormal(tex2D(_Tex, UV), _Scale)

You may need to add another line for this to work.

#pragma target 3.0
#include “UnityStandardUtils.cginc”

4 Likes

That doesn’t seem to work for me:

I included the above and I have tried

o.Normal = UnpackNormal(tex2D(_Nor1, IN.uv_Tex1 * _TexScale1), _Scale)

and I get a Shader error:

Shader error in ‘Custom/Planet’: too many parameters in function call at line 195 (on d3d9)

I have also tried:

o.Normal = UnpackNormal(tex2D(_Nor1, IN.uv_Tex1 * _TexScale1) * _NorPow1 * _Scale)

Shader error in ‘Custom/Planet’: undefined variable “_Scale” at line 195 (on d3d9)

and a few other methods and I can’t seem to get it too work.

Sorry but I am fairly inept when it comes to shading. I am using 6 normal maps as well to shade an entire planet.

This is what I currently have for my normal maps that only seems to work with the normal power float.

o.Normal = UnpackNormal(tex2D(_Nor1, IN.uv_Tex1 * _TexScale1) * _NorPow1) * IN.color.r + UnpackNormal(tex2D(_Nor2, IN.uv_Tex1 * _TexScale2) * _NorPow2) * IN.color.g
            + UnpackNormal(tex2D(_Nor3, IN.uv_Tex1 * _TexScale3) * _NorPow3) * IN.color.b + UnpackNormal(tex2D(_Nor4, IN.uv_Tex1 * _TexScale4) * _NorPow4) * IN.color.a
            + UnpackNormal(tex2D(_Nor5, IN.uv_Tex1 * _TexScale5) * _NorPow5) * IN.uv4_Tex2.x + UnpackNormal(tex2D(_Nor6, IN.uv_Tex1 * _TexScale6) * _NorPow6) * IN.uv4_Tex2.y;

The function is Unpack**Scale**Normal, not UnpackNormal if you want to apply scaling. The one you’re using only takes a single parameter, a the sampled texture value, while the other takes that same texture and a second scale value you provide. This is the function the Standard shader uses.

In my example, _Scale is a stand in for a variable that you define the name for. In your case it could be the _NorPow# variables you’re already using.

1 Like

Hey thanks so much @bgolus

That works and looks a heck of a lot more better than what I had before!

I can’t believe I missed that small detail. I originally thought the _Scale variable came from “UnityStandardUtils.cginc”.

I need to really take some courses on writing shaders because I am a little way over my head with this shading code and I feel rather clumsy on not realizing that it was UnpackScaleNormal!

I would really love to understand shaders more coherently. So I am going to buy some Unity books on shaders.

Can one of you experts in this field recommend some good courses to take? It’s truly fascinating to me. How tiny details make a big difference…I can’t get enough of shaders!