NPR卡通渲染

在UNITY商店下了个免费的琥珀酱的model(好像叫UNITY CHAN,有兴趣的可以自己下载玩玩),发现作者已经写了几个shader,效果看起来还不错,不过不太完全,试着自己补一补

这是用标准着色器的效果,光照信息多了之后真是丑的不行……咱改改。不用bli-phong光照模型,用我们卡通逻辑的光照模型。

第一版shader

Shader "UnityChan-Self/Clohting" {
	Properties {
		_MainTex ("Diffuse", 2D)= "white" {} //albedo材质,定义底色
		_FallOffSampler ("Falloff Control", 2D) = "white" {} //光照衰减取样
		_FALLOFF_POWER ("Falloff Power", Float) = 0.3 //控制光照衰减取样强度
	}

	SubShader {
		Tags {
			"RenderType" = "Opaque"		
			"Queue" = "Geometry"
			"LightMode" = "ForwardBase"
		}

		Pass {
			Cull Back
			ZTest LEqual

			CGPROGRAM
			#pragma multi_compile_fwdbase
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			#include "AutoLight.cginc"

			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _FallOffSampler;
			float _FALLOFF_POWER;

			struct v2f {
				float4 pos : SV_POSITION;
				float2 uv : TEXCOORD0;
				float3 viewDir : TEXCOORD1;
				float3 normal : TEXCOORD2;
				float3 tangent : TEXCOORD3;
				float3 binormal : TEXCOORD4;
				float3 lightDir : TEXCOORD5;
			};

			v2f vert(appdata_tan v) {
				v2f o;

				o.pos = UnityObjectToClipPos(v.vertex);
				o.uv.xy = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
				//_Object2World是针对四维向量的,如果要用法线直接乘,需要在后面补一个0,我们就直接用内置函数算了
				//o.normal = mul(_Object2World, v.normal).xyz; 
				o.normal = UnityObjectToWorldNormal(v.normal);
				
				half4 worldPos = mul(unity_ObjectToWorld, v.vertex);
				o.viewDir.xyz = normalize(_WorldSpaceCameraPos.xyz - worldPos.xyz).xyz;
				//得到世界空间视线方向

				o.tangent = v.tangent.xyz;
				o.binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
				//副切线在切线空间里

				o.lightDir = WorldSpaceLightDir(v.vertex);

				return o;
			}

			float4 frag(v2f i) : SV_Target {
				float4 diffuseSamplerColor = tex2D(_MainTex, i.uv.xy); //取材质色

				float3 normalVec = i.normal;
				float normalDotEye = dot(i.normal, i.viewDir.xyz); //视线和法线的点乘
				float fallOffU = clamp(1.0 - abs(normalDotEye), 0.02, 0.98); //点乘取反

				float4 fallOffSamplerColor = _FALLOFF_POWER * tex2D(_FallOffSampler, float2(fallOffU, 0.25f)); //取颜色衰减
				float3 shadowColor = diffuseSamplerColor.rgb * diffuseSamplerColor.rgb; //将材质色平方,得到一个深色
				float3 combinedColor = lerp(diffuseSamplerColor.rgb, shadowColor, fallOffSamplerColor.r); //根据颜色衰减,取材质色到深色的中间色
				combinedColor *= (1.0 + fallOffSamplerColor.rgb * fallOffSamplerColor.a); //一定程度补偿深色变亮

				return float4(combinedColor.rgb, 1.0);
			}

			ENDCG
		}


	}

	FallBack "Transparent/Cutout/Diffuse"
}

我们不用viewDir和lightDir的dot来计算光照效果,而是直接用viewDir和normal的点乘,这就有点像随时有个光源跟随着摄像机移动,减少光照的细节度,增加底层颜色的突出程度


直接把点乘结果输出的效果,有点像菲涅尔反射2333,不过是简化版本的。越白的地方意味着光照衰减取样越靠右

加上我们画好的光照衰减取样图,越左数值越靠近(0,0,0),越右越靠近(1,1,1),中间的渐变是日式卡通的特点,一般会有一个过渡色而不是硬过渡。说个题外话,实际上存储这个信息,只需要rgba四通道里的任意一个(我们实际上只需要一个0-1的float而不是现在的Vector3),所以可以往里面继续塞来存储其他信息

和底色混合后(0取原底色,1取原底色的平方),效果是这样的

其实讲究的日式卡通渲染应该是有两张底色的,一张作为“明”,一张作为“阴”,就是底色和阴影两张,不过我们没有,就直接把底色的开平方当做阴影色了。(有些更讲究的卡通渲染用的是3张底色图甚至4张)

感觉阴的颜色有点过了,用FALLOFF_POWER把阴的地方的数值稍微压一压,再加数值,做一定的亮度补偿,看自己感觉调整吧,毕竟NPR就是个 很主观的玄学玩意儿……

最后效果如图


然后做一个高光,依然是无视光源方向,把摄像机方向当做光源方向来运算

代码在fragme

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值