Cartoon CG:卡通渲染(tone-based-shading)

    常用的卡通渲染技术中,还有一个tone-based-shading,顾名思义基于色调的渲染技术,具体什么意义呢?比较官方的介绍如下:

     tone-based-shading详解

     In a colored medium such as air-brush and pen, artists often use both hue and luminance (grayscale intensity) shifts. Adding black and white to a given color results in what artists call shades in the case of black and tints in the case of white. When color scales are created by adding gray to a certain color they are called tones  [3]. Such tones vary in hue but do not typically vary much in luminance. Adding the complement of a color can also create tones. Tones are considered a crucial concept to illustrators and are especially useful when the illustrator is restricted to a small luminance range [21]. Another quality of color used by artists is the temperature of the color. The temperature of a color is defined as being warm (red, orange, and yellow), cool (blue, violet, and green), or temperate (red-violets and yellow-greens). The depth cue comes from the perception that cool colors recede whereas warm colors advance. In addition, object colors change temperature in sunlit scenes because cool skylight and warm sunlight vary in relative contribution across the surface, so there may be ecological reasons to expect humans to be sensitive to color temperature variation. Not only is the temperature of a hue dependent upon the hue itself, but this advancing and receding relationship is effected by proximity [5]. Gooch et al. used these techniques and their psychophysical relationship as the basis for their shading model.

      在色彩学中,艺术家们将颜色定义为暖色调(红色、橙色、黄色)和冷色调(蓝色、紫色、绿色),当然了介于其间的温和色(樱桃红、黄绿色),具体参考颜色系标准,或者找你们组里主美或者ui问问。而这些颜色系在着色渲染中存在一些相互影响的关系,比如经典的关系如下:

      The classic computer graphics shading model can be generalized to experiment with tones by using the cosine term (l . n) of Equation 4.1 to blend between two RGB colors, kcool and kwarm:

       

       这个介绍也很简介明了,首先定义冷暖色系,然后根据上面公式计算颜色权重插值。

       非光线追踪的PBR渲染中,公式也都是经验总结,我们看公式的时候最多只能推算返回值的走势或趋势。

       在NPR中,就全是经验总结了,上诉公式中L为light emission direction,也就是光线照射方向,和我们平时用的WorldSpaceLightDir相反,n为顶点单位法向量,所以“阳光直射顶点”时,L·N等于-1,所以I就为Kwarm暖色;如果“阳光照射地平线”,L·N等于0,I为0.5Kcool+0.5Kwarm,既冷暖混合;如果是“地球的背面”,那么L·N等于1,则为Kcool冷色调。

       当然还是要在shader中实现一下的,如下:

Shader "Custom/ToneBasedShader"
{
	Properties
	{
		_CoolColor("Cool Color",Color) = (1,1,1,1)
		_WarmColor("Warm Color",Color) = (1,1,1,1)
		_Extsn("Extsn Value", Range(0,1)) = 1
		_OutColor("Outline Color", Color) = (0,0,0,1)
	}
		SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"
			#include "Lighting.cginc"

			struct appdata
			{
				float4 normal : NORMAL;
				float4 vertex : POSITION;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldP2S : TEXCOORD1;
			};

			float4 _CoolColor;
			float4 _WarmColor;
			float _CoolAlpha;
			float _WarmBeta;

			v2f vert(appdata v)
			{
				v2f o;
				o.worldNormal = UnityObjectToWorldNormal(v.normal.xyz);
				o.worldP2S = normalize(WorldSpaceLightDir(v.vertex));
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				float LN = dot(i.worldNormal ,-i.worldP2S);
				float4 I = (0.5 + LN * 0.5)*_CoolColor + (0.5 - LN * 0.5)*_WarmColor;
				return I;
			}
			ENDCG
		}

		Pass
		{
			Cull front
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float4 normal : NORMAL;
			};

			struct v2f
			{
				float4 vertex : SV_POSITION;
			};

			float _Extsn;
			float4 _OutColor;

			v2f vert(appdata v)
			{
				v2f o;
				v.vertex += v.normal * _Extsn;
				o.vertex = UnityObjectToClipPos(v.vertex);
				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				fixed4 col = _OutColor;
				return col;
			}
			ENDCG
		}
	}
}

         效果如下:

   

          感觉不怎么“卡通”,那是因为这只是第一步,选择蓝色黄色这两中色调只是处理了色调的过度,其他颜色加权没有处理。

          The blue-to-yellow tones range from a fully saturated blue: kblue = (0,0,b), b is in the range [0,1] in RGB space to a fully saturated yellow: kyellow = (y,y,0), y is in the range [0,1] . This produces a very sculpted but unnatural image and is independent of the object's diffuse reflectance kd. The extreme tone related to kd is a variation of diffuse shading where kcool is pure black and kwarm = kd. This would look much like traditional diffuse shading, but the entire object would vary in luminance, including where l . n < 0. A compromise between these strategies will result in a combination of tone scaled object-color and a cool-to-warm undertone, an effect which artists achieve by combining pigments. The undertones can be simulated by a linear blend between the blue/yellow and black/object-color tones:

           

         如果只存在冷暖色系渐变,那模型本身的颜色得不到体现,所以冷暖色进行一个颜色加权,Kd为黑色到模型本身颜色的渐变,使用L·N处理权重,α、β为系数,我们还是shader处理,如下:

fixed4 frag (v2f i) : SV_Target
{
	float LN = dot(-i.worldP2S,i.worldNormal);
	_MainColor = _MainColor * (1 - LN) + _BackColor * LN;
	return _MainColor;
}

          先shader处理底色到模型本身颜色的渐变,效果如下:

  

          然后处理加权,代码如下:

fixed4 frag (v2f i) : SV_Target
{
	float LN = dot(-i.worldP2S,i.worldNormal);
	_MainColor = _MainColor * (1 - LN) + _BackColor * LN;
	_CoolColor = _CoolColor + _CoolAlpha * _MainColor;
	_WarmColor = _WarmColor + _WarmBeta * _MainColor;
	float4 I = (0.5 + LN * 0.5)*_CoolColor + (0.5 - LN * 0.5)*_WarmColor;
	return I;
}

         效果如下:

  

        效果看起来倒是可以,但是少了高光部分,我们接下来加上高光代码:

float3 spec = _ShiniFactor * _LightColor0.rgb* pow(max(dot(i.worldNormal, i.worldHDir), 0), _ShiniPow);
I += fixed4(spec, 1);

        加上了传统光照模型中的specular分量部分,效果如下:

  

        可以看得出来加上光照模型部分计算之后,搞得四不像了,看来NPR和PBR结合确实不怎么好看,所以我们改成一个具有“艺术风格”的高光,卡通高光的特点就是光斑“清晰明了”,核心就是色阶梯度少,边界清晰,所以我们是用一些方法处理色阶的变化,代码如下:

float3 specular = _LightColor0.rgb * _SpecFactor * step(_SpecThre, dot(i.worldNormal, i.worldHDir));
return I + fixed4(specular, 1);

        用一个阀值_SpecThreshold控制当光照模型中specular计算参数H·N超过的时候返回1,反之0,则只渲染强度超过_SpecThreshold的片段,效果如下:

  

         可以看的出来,这个光斑太毛糙了,我们还需要进行边缘平滑插值处理,做法就是将“边缘”H·N约等于0的地方进行平滑插值,代码如下:

float HN = dot(worldNormal, worldHDir);
float edge = fwidth(HN);
float specparam = saturate(smoothstep(-edge, edge, HN - _SpecThre));
float3 specular = _LightColor0.rgb * _SpecFactor * specparam;
return I + fixed4(specular, 1);

         fwidth可以得到当前像素和其相邻像素在屏幕空间的变化程度,属于线性插值变化,所以这个函数可以帮我们处理世界空间中因光照随角度变化产生的非线性影响,参考:fwidth,效果如下:

  

         看得出来圆形光斑不够圆滑,这是因为我们在顶点函数中处理的几何向量计算,移到片段函数中即可,顶点光照效率高,但是片段光照计算效果好,最终效果如下:

  

        最后,卡通渲染还有很多细节处理方式,我们后面继续。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值