(For more resources related to this topic, see here.)
The specularity of an object surface simply describes how shiny it is. These types of effects are often referred to as view-dependent effects in the Shader world. This is because in order to achieve a realistic Specular effect in your Shaders, you need to include the direction the camera or user is facing the object’s surface. Although Specular requires one more component to achieve its visual believability, which is the light direction. By combining these two directions or vectors, we end up with a hotspot or highlight on the surface of the object, half way between the view direction and the light direction. This half-way direction is called the half vector and is something new we are going to explore in this article, along with customizing our Specular effects to simulate metallic and cloth Specular surfaces.
Utilizing Unity3D’s built-in Specular type
Unity has already provided us with a Specular function we can use for our Shaders. It is called the BlinnPhong Specular lighting model. It is one of the more basic and efficient forms of Specular, which you can find used in a lot of games even today. Since it is already built into the Unity Surface Shader language, we thought it is best to start with that first and build on it. You can also find an example in the Unity reference manual, but we will go into a bit more depth with it and explain where the data is coming from and why it is working the way it is. This will help you to get a nice grounding in setting up Specular, so that we can build on that knowledge in the future recipes in this article.
Let’s start by carrying out the following:
- Create a new Shader and give it a name.
- Create a new Material, give it a name, and assign the new Shader to its shaper property.
- Then create a sphere object and place it roughly at world center.
- Finally, let’s create a directional light to cast some light onto our object.
When your assets have been set up in Unity, you should have a scene that resembles the following screenshot:
How to do it…
- Begin by adding the following properties to the Shader’s Properties block:
- We then need to make sure we add the variables to the CGPROGRAM block, so that we can use the data in our new properties inside our Shader’s CGPROGRAM block. Notice that we don’t need to declare the _SpecColor property as a variable. This is because Unity has already created this variable for us in the built-in Specular model. All we need to do is declare it in our Properties block and it will pass the data along to the surf() function.
- Our Shader now needs to be told which lighting model we want to use to light our model with. You have seen the Lambert lighting model and how to make your own lighting model, but we haven’t seen the BlinnPhong lighting model yet. So, let’s add BlinnPhong to our #pragma statement like so:
- We then need to modify our surf() function to look like the following:
How it works…
This basic Specular is a great starting point when you are prototyping your Shaders, as you can get a lot accomplished in terms of writing the core functionality of the Shader, while not having to worry about the basic lighting functions.
Unity has provided us with a lighting model that has already taken the task of creating your Specular lighting for you. If you look into the UnityCG.cginc file found in your Unity’s install directory under the Data folder, you will notice that you have Lambert and BlinnPhong lighting models available for you to use. The moment you compile your Shader with the #pragma surface surf BlinnPhong, you are telling the Shader to utilize the BlinnPhong lighting function in the UnityCG.cginc file, so that we don’t have to write that code over and over again.
With your Shader compiled and no errors present, you should see a result similar to the following screenshot:
Creating a Phong Specular type
The most basic and performance-friendly Specular type is the Phong Specular effect. It is the calculation of the light direction reflecting off of the surface compared to the user’s view direction. It is a very common Specular model used in many applications, from games to movies. While it isn’t the most realistic in terms of accurately modeling the reflected Specular, it gives a great approximation that performs well in most situations. Plus, if your object is further away from the camera and the need for a very accurate Specular isn’t needed, this is a great way to provide a Specular effect on your Shaders.
In this article, we will be covering how to implement the per vertex version of the and also see how to implement the per pixel version using some new parameters in the surface Shader’s Input struct. We will see the difference and discuss when and why to use these two different implementations for different situations.
- Create a new Shader, Material, and object, and give them appropriate names so that you can find them later.
- Finally, attach the Shader to the Material and the Material to the object. To finish off your new scene, create a new directional light so that we can see our Specular effect as we code it.
How to do it…
- You might be seeing a pattern at this point, but we always like to start out with our most basic part of the Shader writing process: the creation of properties. So, let’s add the following properties to the Shader:
- We then have to make sure to add the corresponding variables to our CGPROGRAM block inside our SubShader block.
- Now we have to add our custom lighting model so that we can compute our own Phong Specular. Add the following code to the Shader’s SubShader() function. Don’t worry if it doesn’t make sense at this point; we will cover each line of code in the next section:
- Finally, we have to tell the CGPROGRAM block that it needs to use our custom lighting function instead of one of the built-in ones. We do this by changing the #pragma statement to the following:
The following screenshot demonstrates the result of our custom Phong lighting model using our own custom reflection vector:
How it works…
Let’s break down the lighting function by itself, as the rest of the Shader should be pretty familiar to you at this point.
We simply start by using the lighting function that gives us the view direction. Remember that Unity has given you a set of lighting functions that you can use, but in order to use them correctly you have to have the same arguments they provide. Refer to the following table, or go to http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLighting.html:
Not view Dependent
half4 Lighting Name You choose (SurfaceOutput s, half3 lightDir, half atten);
half4 Lighting Name You choose (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten);
In our case, we are doing a Specular Shader, so we need to have the view-dependent lighting function structure. So, we have to write:
This will tell the Shader that we want to create our own view-dependent Shader. Always make sure that your lighting function name is the same in your lighting function declaration and the #pragma statement, or Unity will not be able to find your lighting model.
The lighting function then begins by declaring the usual Diffuse component by dotting the vertex normal with the light direction or vector. This will give us a value of 1 when a normal on the model is facing towards the light, and a value of -1 when facing away from the light direction.
We then calculate the reflection vector taking the vertex normal, scaling it by 2.0 and by the diff value, then subtracting the light direction from it. This has the effect of bending the normal towards the light; so as a vertex normal is pointing away from the light, it is forced to look at the light. Refer to the following screenshot for a more visual representation. The script that produces this debug effect is included at the book’s support page at www.packtpub.com/support.
Then all we have left to do is to create the final spec’s value and color. To do this, we dot the reflection vector with the view direction and take it to a power of _SpecPower. Finally, we just multiply the _SpecularColor.rgb value over the spec value to get our final Specular highlight.
The following screenshot displays the final result of our Phong Specular calculation isolated out in the Shader:
Creating a BlinnPhong Specular type
Blinn is another more efficient way of calculating and estimating specularity. It is done by getting the half vector from the view direction and the light direction. It was brought into the world of Cg by a man named Jim Blinn. He found that it was much more efficient to just get the half vector instead of calculating our own reflection vectors. It cut down on both code and processing time. If you actually look at the built-in BlinnPhong lighting model included in the UnityCG.cginc file, you will notice that it is using the half vector as well, hence the reason why it is named BlinnPhong. It is just a simpler version of the full Phong calculation.
- This time, instead of creating a whole new scene, let’s just use the objects and scene we have, and create a new Shader and Material and name them BlinnPhong.
- Once you have a new Shader, double-click on it to launch MonoDevelop, so that we can start to edit our Shader.
How to do it…
- First, we need to add our own properties to the Properties block, so that we can control the look of the Specular highlight.
- Then, we need to make sure that we have created the corresponding variables inside our CGPROGRAM block, so that we can access the data from our Properties block, inside of our subshader.
- Now it’s time to create our custom lighting model that will process our Diffuse and Specular calculations.
- To complete our Shader, we will need to tell our CGPROGRAM block to use our custom lighting model rather than a built-in one, by modifying the #pragma statement with the following code:
The following screenshot demonstrates the results of our BlinnPhong lighting model:
How it works…
The BlinnPhong Specular is almost exactly like the Phong Specular, except that it is more efficient because it uses less code to achieve almost the same effect. You will find this approach nine times out of ten in today’s modern Shaders, as it is easier to code and lighter on the Shader performance.
Instead of calculating our own reflection vector, we are simply going to get the vector half way between the view direction and the light direction, basically simulating the reflection vector. It has actually been found that this approach is more physically accurate than the last approach, but we thought it is necessary to show you all the possibilities.
So to get the half vector, we simply need to add the view direction and the light direction together, as shown in the following code snippet:
Then, we simply need to dot the vertex normal with that new half vector to get our main Specular value. After that, we just take it to a power of _SpecPower and multiply it by the Specular color variable. It’s much lighter on the code and much lighter on the math, but still gives us a nice Specular highlight that will work for a lot of real-time situations.
Masking Specular with textures
Now that we have taken a look at how to create a Specular effect for our Shaders, let’s start to take a look into the ways in which we can start to modify our Specular and give more artistic control over its final visual quality. In this next recipe, we will look at how we can use textures to drive our Specular and Specular power attributes.
The technique of using Specular textures is seen in most modern game development pipelines because it allows the 3D artists to control the final visual effect on a per-pixel basis. This provides us with a way in which we can have a mat-type surface and a shiny surface all in one Shader; or, we can drive the width of the Specular or the Specular power with another texture, to have one surface with a broad Specular highlight and another surface with a very sharp, tiny highlight.
There are many effects one can achieve by mixing his/her Shader calculations with textures, and giving artists the ability to control their Shader’s final visual effect is key to an efficient pipeline. Let’s see how we can use textures to drive our Specular lighting models. This article will introduce you to some new concepts, such as creating your own Input struct, and learning how the data is being passed around from the output struct, to the lighting function, to the Input struct, and to the surf() function. Understanding the flow of data between these core Surface Shader elements is core to a successful Shader pipeline.
- We will need a new Shader, Material, and another object to apply our Shader and Material on to.
- With the Shader and Material connected and assigned to your object in your scene, double-click the Shader to bring it up in MonoDevelop.
- We will also need a Specular texture to use. Any texture will do as long as it has some nice variation in colors and patterns. The following screenshot shows the textures we are using for this recipe:
How to do it…
- First, let’s populate our Properties block with some new properties. Add the following code to your Shader’s Properties block:
- We then need to add the corresponding variables to the subshader, so that we can access the data from the properties in our Properties block. Add the following code, just after the #pragma statement:
- Now we have to add our own custom Output struct. This will allow us to store more data for use between our surf function and our lighting model. Don’t worry if this doesn’t make sense just yet. We will cover the finer details of this Output struct in the next section of the article. Place the following code just after the variables in the SubShader block:
- Just after the Output struct we just entered, we need to add our custom lighting model. In this case, we have a custom lighting model called LightingCustomPhong. Enter the following code just after the Output struct we just created:
- In order for our custom lighting model to work, we have to tell the SubShader block which lighting model we want to use. Enter the following code to the #pragma statement so that it loads our custom lighting model:
- Since we are going to be using a texture to modify the values of our base Specular calculation, we need to store another set of UVs for that texture specifically. This is done inside the Input struct by placing the word uv in front of the variable’s name that is holding the texture. Enter the following code just after your custom lighting model:
- To finish off the Shader, we just need to modify our surf() function with the following code. This will let us pass the texture information to our lighting model function, so that we can use the pixel values of the texture to modify our Specular values in the lighting model function:
The following screenshot shows the result of masking our Specular calculations with a color texture and its channel information. We now have a nice variation in Specular over the entire surface, instead of just a global value for the Specular: