Unity makes two-dimensional cartoon rendering character material - 3. Specular reflection and ILM texture

Unity makes two-dimensional material characters


back to catalog

Hello everyone, I am Zhao.
Here we continue to talk about the material of the two-dimensional character. Last time I talked about the color gradation of light and shadow, and this time I will continue to talk about the effect of the lighting model.
As we said before, the final effect of the lighting model is:
ambient color + diffuse reflection + highlight + reflection.
Here we can ignore the ambient light first, and then do diffuse reflection before, using HalfLambert, and the rest is highlights and reflections

1. High light

Continue to apply the specular lighting model learned before, and it is customary to use BlinnPhong.

//获取BlinnPhong高光
float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
{
float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
float specDir = max(dot(normalize(worldNormal), halfDir), 0);
float specVal = pow(specDir, _shininess);
return specVal;
}
half4 frag (v2f i) : SV_Target
{
    half4 col = tex2D(_BaseMap, i.uv);
	half4 sssCol = tex2D(_SSSMap, i.uv);

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);
	//高光
	half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);	
	half3 finalRGB = col.rgb*toonVal + sssCol * (1 - toonVal) + _specColor * specVal;
	half alpha = col.a;
    return half4(finalRGB,alpha);
}

insert image description here

After adding the highlights, the model becomes like this, the highlights are a bit too strong. Don't worry about it, add all the effects, and adjust it at the end.

Two, reflection

Friends who are familiar with me know that when adding reflection effects, Azhao generally recommends Matcap, especially in this situation where true reflection is not needed, but a little false reflection of the environment is required.
So I found the code of Matcap and added it.

float2 GetMatCapUV(float3 normalWorld)
{
	float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
	return normalView.xy*0.5 + 0.5;
}
half4 frag (v2f i) : SV_Target
{
    // sample the texture
    half4 col = tex2D(_BaseMap, i.uv);
	half4 sssCol = tex2D(_SSSMap, i.uv);

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);

	//高光
	half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);
	//matcap
	float2 MatCapUV = GetMatCapUV(i.worldNormal)*_MatCapUVScale;
	float4 MatCapCol = tex2D(_MatCapTex, MatCapUV)*_MatCapIntensity;
	MatCapCol = pow(MatCapCol, _MatCapPow);
				

	half3 finalRGB = col.rgb*toonVal + sssCol  * (1 - toonVal)+_specColor* specVal;
	finalRGB = finalRGB * MatCapCol.rgb;

	half alpha = col.a;
    return half4(finalRGB,alpha);
}

insert image description here

For this effect, BlinnPhong highlights and Matcap reflections have been added. But the obvious effect is wrong, the character looks like an eighteen bronze statue of Shaolin Temple.

3. ILM map

After adding the highlights and reflections above, I found that the effect was wrong. So what needs to be corrected?
We can analyze what is the reason for the wrong effect:
1. The reflection intensity of some parts is wrong, such as the skin, which should not reflect so strongly.
2. The range of highlights is
wrong. The range of the reflection is wrong. The reflection should not have the same intensity everywhere. It should be displayed according to the actual material. For example, the skin does not reflect, the leather clothing may have a little bit, and the metallic paint part of the guitar should have a relatively strong reflection.
So, how should we control these intensities and ranges? Here we need to review the actual situation of the ILM map obtained when analyzing the resources before:
Since we already know that the A channel of the ILM map is an inner line, we ignore it here and only look at the three channels of RGB:
the first is the R channel:
insert image description here

Then the G channel:
insert image description here

Then the B channel:
insert image description here

Here you can guess the role of the three channels.
The more obvious one is the R channel, which obviously controls the intensity of highlights.
The role of the G channel is not very obvious, only the face and some parts of the body are relatively white, and the other colors are the same. This should control the intensity of the shadow.
Only some parts of the B channel are lit, and most of them are black. It can be understood that it is used to control the shape range of highlights and reflections.
So we can add the value of the channel corresponding to the ILM map to the previous highlights and reflections.
First look at the highlight part:

half4 frag (v2f i) : SV_Target
{
    // sample the texture
    half4 col = tex2D(_BaseMap, i.uv);
	half4 sssCol = tex2D(_SSSMap, i.uv);
	half4 ilmCol = tex2D(_ILMMap, i.uv);

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);
	//高光
	half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);
	half3 finalRGB = col.rgb*toonVal + sssCol  * (1 - toonVal)+_specColor* specVal*ilmCol.r+ _specColor * specVal*ilmCol.b*_SpecAdd;		

	half alpha = col.a;
    return half4(finalRGB,alpha);
}

Use the R channel of the ILM map to control the highlight intensity. Originally, the B channel of the ILM map controls the shape. I modified the usage here. After the R channel controls the intensity, use the B channel to strengthen the highlights in certain areas. , such as the light spots on the chest, the high light spots on the metal edge of the clothes, etc. So you get this effect:
insert image description here

Let's look at the Matcap part again, directly use the B channel to control the range, in order to see it more clearly, so temporarily remove the highlights:

half4 frag (v2f i) : SV_Target
{
    // sample the texture
    half4 col = tex2D(_BaseMap, i.uv);
	half4 sssCol = tex2D(_SSSMap, i.uv);
	half4 ilmCol = tex2D(_ILMMap, i.uv);

	//色阶化
	half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
	half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);


	float2 MatCapUV = GetMatCapUV(i.worldNormal)*_MatCapUVScale;
	float4 MatCapCol = tex2D(_MatCapTex, MatCapUV)*_MatCapIntensity;
	MatCapCol = pow(MatCapCol, _MatCapPow);



	half3 finalRGB = col.rgb*toonVal + sssCol * (1 - toonVal);
	finalRGB = finalRGB * (1-ilmCol.b) +MatCapCol.rgb*ilmCol.b;

	half alpha = col.a;
    return half4(finalRGB,alpha);
}

insert image description here

At this time, it can be seen that the general body reflex like the eighteen bronze figures before has disappeared, and only the designated parts have the reflex effect.
Finally, adding ILM's effects on highlights and reflections together, we get:

insert image description here

4. Complete Shader

Shader "azhao/ToonBodyLight"
{
    Properties
    {
        _BaseMap ("BaseMap", 2D) = "white" {}
		_SSSMap("SSSMap", 2D) = "white" {}
		_ILMMap("ILMMap", 2D) = "white" {}

		_specColor("specColor",Color) = (1,1,1,1)
		_shininess("shininess", Range(1 , 100)) = 1
		_SpecAdd("SpecAdd",float) = 1.0


		_GradationMin("GradationMin",Range(0.0,1.0)) = 0.0
		_GradationMax("GradationMax",Range(0.0,1.0)) = 1.0


		_MatCapTex("MatCapTex", 2D) = "white" {}
		_MatCapIntensity("MatCapIntensity",Range(0,2)) = 1
		_MatCapPow("MatCapPow",Range(0,5)) = 1
		_MatCapUVScale("MatCapUVScale",Range(0,1)) = 1

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
			#pragma multi_compile_fwdbase
			#include "AutoLight.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
				float2 uv2 : TEXCOORD1;
				float3 normal:NORMAL;
            };

            struct v2f
            {                
                float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float2 uv2 : TEXCOORD1;
				float3 worldPos :TEXCOORD2;
				float3 worldNormal :TEXCOORD3;
            };

            sampler2D _BaseMap;
            float4 _BaseMap_ST;
			sampler2D _SSSMap;
			sampler2D _ILMMap;

			float4 _specColor;
			float _shininess;

			float _SpecAdd;
			float _GradationMin;
			float _GradationMax;
			sampler2D _MatCapTex;
			float _MatCapIntensity;
			float _MatCapPow;
			float _MatCapUVScale;

			//获取HalfLambert漫反射值
			float GetHalfLambertDiffuse(float3 worldPos, float3 worldNormal)
			{
				float3 lightDir = UnityWorldSpaceLightDir(worldPos);
				float NDotL = saturate(dot(worldNormal, lightDir));
				float halfVal = NDotL * 0.5 + 0.5;
				return halfVal;
			}

			//获取BlinnPhong高光
			float GetBlinnPhongSpec(float3 worldPos, float3 worldNormal)
			{
				float3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				float3 halfDir = normalize((viewDir + _WorldSpaceLightPos0.xyz));
				float specDir = max(dot(normalize(worldNormal), halfDir), 0);
				float specVal = pow(specDir, _shininess);
				return specVal;
			}


			float2 GetMatCapUV(float3 normalWorld)
			{
				float3 normalView = mul(UNITY_MATRIX_IT_MV, normalWorld);
				return normalView.xy*0.5 + 0.5;
			}

            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _BaseMap);
				o.uv2 = TRANSFORM_TEX(v.uv2, _BaseMap);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                // sample the texture
                half4 col = tex2D(_BaseMap, i.uv);
				half4 sssCol = tex2D(_SSSMap, i.uv);
				half4 ilmCol = tex2D(_ILMMap, i.uv);

				//色阶化
				half halfLambert = GetHalfLambertDiffuse(i.worldPos, i.worldNormal);
				half toonVal = smoothstep(_GradationMin, _GradationMax, halfLambert);

				//高光
				half specVal = GetBlinnPhongSpec(i.worldPos, i.worldNormal);
				//matcap
				float2 MatCapUV = GetMatCapUV(i.worldNormal)*_MatCapUVScale;
				float4 MatCapCol = tex2D(_MatCapTex, MatCapUV)*_MatCapIntensity;
				MatCapCol = pow(MatCapCol, _MatCapPow);


				half3 finalRGB = col.rgb*toonVal + sssCol  * (1 - toonVal)+_specColor* specVal*ilmCol.r+ _specColor * specVal*ilmCol.b*_SpecAdd;
				finalRGB = finalRGB * (1-ilmCol.b) +MatCapCol.rgb*ilmCol.b;

				half alpha = col.a;
                return half4(finalRGB,alpha);
            }
            ENDCG
        }

    }
}

Guess you like

Origin blog.csdn.net/liweizhao/article/details/131033315