Sunday 10 April 2016

Diffuse shader: surface shader

In this post I will show you how to create a diffuse shader (which reacts to lighting) using surface shaders.

Surface shaders are intended to simplify the code for creating complex shaders. These type of shaders will then create automatically vertex and fragment functions and we do not need to deal with them.

I will then show you how to do a diffuse shader using vertex and fragment methods, like we did for the unlit shader in the previous post.

Surface shaders are structured differently than vertex/fragment shaders and, in particular, they have some parameteres that are required: a surface function, which is the CG written method that deals with the surface shader and a lighting model. Unity provides pre-made lighting models that can be used in the shader, however, it is possible to create custom ones as well.

Just like in vertex/fragment shaders, we point to the surface method with the #pragma directive, just like so:


1
#pragma surface surf Lambert : optional_parameters

This tells Unity to lok for a function called surf and to use the pre-built lighing model Lambert. It is possible to add additional parameters here, for example, to enable alpha blend.

Then the method itself will have to be declared as such:


1
2
void surf(Input in, inout SurfaceOutput o)
{}

The structure Input is defined by you. Here we will have all the input variables that are needed to achieve whatever effect we trying to create in the shader. We can use some in_built variables to get some information about our model, like world normals ecc. A complete list is of course available on the Unity website (link here).

The SurfaceOutput structure is already pre-built in Unity, and so are its parameters. We can use its variables to set the output of our shaders. Some examples are Albedo, which determines the diffuse color, or Alpha, used for transparency.

Now, this is the complete code used to create a diffuse shader:



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Shader "Custom/SingleColorSurfaceShader" {
    Properties
    {
    _Color("Tint",Color) = (1,1,1,1)

    }

    SubShader {
         
      CGPROGRAM

      #pragma surface surf Lambert 

      float4 _Color;
      fixed _Transparency;

      struct Input {
          float4 col : COLOR;
      };
 

      void surf (Input IN, inout SurfaceOutput o) {
         o.Albedo = _Color.rgb;

     
      }
      ENDCG
    }
   
  }

Just like a  vertex/fragment shader, we start with the keyword Shader, followed by the name. This is ShaderLab, so the syntax is exactly the same. The main difference is in the CG part of the program.

On line 16 we tell the engine where to look for our surface method, and we are usnig the Lambert lighting model.

The structure Input contains only a variable float4 called col. Actually, this variable is not even used, as we are going to output the color the is passed in by the user in the properties. It is necessary to put a variable inside the structure though, otherwise we will get en error.

In our surf method, on line 22, we simply set the Albedo member (the diffuse color) of the SurfaceOutput structure (predefined) to be equal to the _Color public variable.

To sum up, all we are doing here is taking the _Color variable, which is the color selected by the user, and pass it to the SurfaceOutput.Albedo variable, which is a predetermined variable that set the diffuse color of the model.

As you can see, the code is very simple and we can avoid to deal with vertex and fragment methods, which are automatically generated for us behind the scenes.

If we create a material with this shader and attach it to a sphere, this is the result:




No comments:

Post a Comment