您现在的位置是:首页 >其他 >Unity制作二次元卡通渲染角色材质——3、高光反射与ILM贴图网站首页其他

Unity制作二次元卡通渲染角色材质——3、高光反射与ILM贴图

阿赵3D 2024-08-14 12:01:03
简介Unity制作二次元卡通渲染角色材质——3、高光反射与ILM贴图

Unity制作二次元材质角色


回到目录

大家好,我是阿赵。
这里继续来讲二次元角色的材质。上次讲了光影的色阶化问题,这次继续讲光照模型效果的问题。
之前我们说过,光照模型的最后效果是:
环境色+漫反射+高光+反射。
这里我们可以先忽略环境光,然后之前做了漫反射,用的是HalfLambert,剩下的就是高光和反射了

一、高光

继续套用之前学过的高光光照模型,习惯上是使用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);
}

在这里插入图片描述

加上高光之后,模型就变成这样了,高光有点强过头了。先不管,把效果都加上,最后再调。

二、反射

熟悉我的朋友都知道了,加反射效果,阿赵一般是推荐Matcap的,特别是这种不需要真反射,只是要一点环境假反射的情况。
于是又找到Matcap的代码,加上去。

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);
}

在这里插入图片描述

这个效果,已经加上了BlinnPhong高光、Matcap反射。但明显效果是不对的,角色像一个少林寺十八铜人似的。

三、ILM贴图

上面把高光和反射加上去之后,发现效果不对。那么需要怎样去修正呢?
我们可以分析一下,效果不对的原因是什么:
1、某些部位的反射强度不对,比如皮肤,不应该反射这么强烈
2、高光的范围不对,有些部位的高光范围应该被控制在一个局部
3、反射的范围不对,反射不应该所有地方都一样的强度,应该根据实际的材质来显示,比如皮肤是不会反射的,皮衣可能有一点,吉他的金属漆部分应该有比较强的反射。
那么,我们应该怎样去控制这些强度和范围呢?这里我们需要回顾一下,之前分析资源时,得到的ILM贴图的实际情况:
由于已经知道ILM贴图的A通道是内描线,所以这里先忽略,只看RGB三个通道:
首先是R通道:
在这里插入图片描述

然后是G通道:
在这里插入图片描述

然后是B通道:
在这里插入图片描述

这里可以猜测一下,三个通道的作用了。
比较明显的是R通道,他明显是控制高光强弱的。
G通道的作用不是很明显,只有脸部和身上某些部位是比较白,其他的颜色都一样。这样应该是控制影子强度的,
B通道只有某些局部亮起来,大部分都是黑的,可以理解成,是用来控制高光和反射的形状范围的。
于是我们可以把ILM贴图对应的通道的值,加在之前的高光和反射上面。
首先来看高光部分:

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);
}

使用ILM贴图的R通道控制高光强度,本来ILM贴图的B通道是控制形状的,我这里修改了一下用法,变成了在R通道控制完强度之后,使用B通道把某些区域的高光加强一下,比如胸部的光点,衣服上的金属边的高光点等。于是就得到了这样的效果:
在这里插入图片描述

再来看Matcap部分,直接用B通道控制范围,为了看得更清楚一些,所以暂时把高光去掉:

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);
}

在这里插入图片描述

这时候可以看到,之前十八铜人一般的全身反射已经没有了,变成了只有指定的部位出现了反射效果。
最后,把ILM对高光和反射的影响一起加上,就得到了:

在这里插入图片描述

四、完整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
        }

    }
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。