Hi, and welcome to Tutorial 3 of my Unity 5 Shader Programming tutorial. Today we are going to implement an other lighting algorithm called Specular Light. This algorithm builds on the Ambient and Diffuse lighting tutorials, so if you haven’t been trough them, now is the time. 🙂
Specular Light
So far, we got a basic light model to illuminate objects. But, what if we got a blank, polished or shiny object we want to render? Say a metal surface, plastic, glass, bottle and so on? Diffuse light does not include any of the tiny reflections that make a smooth surface shine.
To simulate this shininess, we can use a lighting model named Specular highlights.
Specular highlights calculates another vector that simulates a reflection of a light source, which hits the camera, or “the eye”.
What’s “the eye” vector, you might think? Well, it’s a pretty easy answer to this. It’s the vector that points from our camera position to the camera target.
One way to calculate the specular light is
I=Ai*Ac+Di*Dc*N.L+Si*Sc*(R.V)n
Where
R=2*(N.L)*N-L
This is called the Phong model for specular light.
This model calculates the angle between the Reflection Vector and the View vector. It describes how much of the reflection hits directly on the camera lens.
There is another way of calculating this called the Blinn-Phong model where you don’t need to calculate the reflection vector all the time.
Blinn-Phong?
In Blinn-Phong, instead of calculating the reflection vector R, we calculate the halfway vector between the view and the light direction vector, meaning we can replace the dot product between R and V with the dot product between N and H.
where H:
Then we have a parameter n that describes how rough the surface is.
The biggest visual difference between these two implementations is that while Phong will always have a circular shape, the Blinn-Phong will have an elliptical shape from steep angles. This mimics the real world.
Both models got their pros and cons that we won’t discuss in this article.
Implementation
The implementation is straight forward, nothing new from the previous tutorials. In other words, let’s get started with the source:
Shader "UnityShaderTutorial/Tutorial3SpecularLight-GlobalStates" { SubShader { Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #include "UnityCG.cginc" #pragma target 2.0 #pragma vertex vertexShader #pragma fragment fragmentShader float4 _LightColor0; struct vsIn { float4 position : POSITION; float3 normal : NORMAL; }; struct vsOut { float4 screenPosition : SV_POSITION; float4 position : COORDINATE0; float3 normal : NORMAL; }; vsOut vertexShader(vsIn v) { vsOut o; o.screenPosition = mul(UNITY_MATRIX_MVP, v.position); o.normal = normalize(mul(v.normal, _World2Object)); o.position = v.position; return o; } float4 fragmentShader(vsOut psIn) : SV_Target { float4 ambientLight = UNITY_LIGHTMODEL_AMBIENT; float4 lightDirection = normalize(_WorldSpaceLightPos0); float4 diffuseTerm = saturate( dot(lightDirection, psIn.normal)); float4 diffuseLight = diffuseTerm * _LightColor0; float4 cameraPosition = normalize(float4( _WorldSpaceCameraPos,1) - psIn.position); // Blinn-Phong float4 halfVector = normalize(lightDirection+cameraPosition); float4 specularTerm = pow( saturate( dot( psIn.normal, halfVector)), 25); // Phong //float4 reflectionVector = reflect(-lightDirection, float4(psIn.normal,1)); //float4 specularTerm = pow(saturate(dot(reflectionVector, cameraPosition)),15); return ambientLight + diffuseLight + specularTerm; } ENDCG } } }
There are two main differences here, we need the vertex position in the shader, as well as the code that calculates the updated light equation.
This is just a pass-through from the Vertex Shader.
Then we need to do the specular calculation itself.
First we get the position of the camera by using the built in variable WorldSpaceCameraPos and the vertex position.
Then we calculate the half vector by normalizing the light direction added with camera positon.
The last thing we need to calculate is the specular term itself, H.V – and add it to our existing lighting equation. Here we are using a function called pow(x,y) what raises the specified value X with the specified power Y,
25 is the shininess factor, feel free to play around with the value.
As you can see, we didn’t use any new
Downloads:
Download the source from GitHub here:
https://github.com/petriw/UnityShaderProgramming/tree/master/3 – Specular Light
Hi.. These tuts are really informative and helped me understand unity shader programming. I was actually looking for some help on unity 5 double sided shader., and saw these post. If you could do one more tut. for a double sided shader for Unity 5, please do it. It will be a great help.
Thanks 🙂
Hello,
I have a question, Why do you use negative lightDirection when you calculate the reflectionVector in the Phong model?
Thanks. Great tutorial!!!
This website is just gret. I’ve search these info
a long time and I realised that is good written, easy to understand.
I congratulate you for this article that I am going to recommend to people around.
I ask you to go to the gpa-calculator.co site where each pupil or pupil can find ratings grade
point average marks. All good!