UnityShader-素描铅笔画风格 实现详解

10 篇文章 0 订阅
8 篇文章 2 订阅
效果图

在这里插入图片描述

实现大致思路
(1)几张铅笔画程度不同的素描原线图

在这里插入图片描述

(2)根据模型顶点法线与场景中光照方向的夹角算点积。
(3)点积越大,颜色越前,素描程度越低,点积越小,颜色越深,素描程度越大。
(4)将点积分为七个不同的范围,其中最大的范围为纯白色
(5)其余六个范围,按点积大小从上方素描原线图由浅至深进行采样
(6)增加一个纯白贡献度,将不采样的部分及单次采样的部分向白色翻转,形成留白的效果

详细代码解释
(1) 定义属性
	Properties{
		//物体本身颜色
		_Color("Main Color",COLOR) = (1,1,1,1)
		//采样纹理平铺程度,越大则采样越细
		_TileFactor("TileFactor",Float) = 8
		//六张素描原线图纹理,按颜色深浅赋值
		_Hatch0("Hatch0",2D) = "white" {}
		_Hatch1("Hatch1",2D) = "white" {}
		_Hatch2("Hatch2",2D) = "white" {}
		_Hatch3("Hatch3",2D) = "white" {}
		_Hatch4("Hatch4",2D) = "white" {}
		_Hatch5("Hatch5",2D) = "white" {}
	}
(2)顶点及片段结构体数据
struct a2v {
	//模型空间下的顶点坐标
	float4 vertex : POSITION;
	//模型空间下的法线坐标
	float4 normal : NORMAL;
	//模型采样纹理坐标
	float2 texcoord : TEXCOORD0;
};

struct v2f {
	//裁剪空间下的坐标
	float4 pos : SV_POSITION;
	//纹理采样坐标
	float2 uv :TEXCOORD0;
	//这两个变量是记录六张素描纹理的采样程度,两个float3的x,y,z分别记录不同纹理的采样程度
	float3 hatchWeight0:TEXCOORD1;
	float3 hatchWeight1:TEXCOORD2;
};
(3)实现顶点着色器
v2f vert(a2v i) {
	v2f o;
	o.pos = UnityObjectToClipPos(i.vertex);
	o.uv = i.texcoord.xy * _TileFactor;
	fixed3 worldLightDir = normalize(WorldSpaceLightDir(i.vertex));
	fixed3 worldNormal = UnityObjectToWorldNormal(i.normal);
	//计算世界光照方向和模型法线的点积,并且规范到0-1之间,
	//夹角越小,点积越大,这个法线与世界光照方向重合程度越高,则越亮,素描程度越低
	//夹角越大,点积越小,这个法线与世界光照方向重合程度越低,则越暗,素描程度越高
	//当然这里你也可以使用其他的坐标,比如视线方向与法线,效果并不尽相同,根据需求来
	float diff = max(0, dot(worldLightDir , worldNormal));
	//我们有七个范围,最大的范围为白色,其他六个范围在不同的素描原线纹理中进行采样
	float hatchFactor = diff * 7.0;
	//初始化
	o.hatchWeight0 = fixed3(0, 0, 0);
	o.hatchWeight1 = fixed3(0, 0, 0);
	if (hatchFactor > 6.0) {
		//到达这个区间内的颜色为纯白
		//最终要做一个转化,也就是利用纯白的贡献度
	}
	else if (hatchFactor > 5.0) {
		//最浅的那张颜色
		o.hatchWeight0.x = hatchFactor - 5.0;
	}
	else if (hatchFactor > 4.0) {
		o.hatchWeight0.x = hatchFactor - 4.0;
		o.hatchWeight0.y = 1 - o.hatchWeight0.x;
	}
	else if (hatchFactor > 3.0) {
		//这里及后面都计算了两个值,同时采样上一张,和当前素描纹理,是为了做一个过度
		o.hatchWeight0.y = hatchFactor - 3.0;
		o.hatchWeight0.z = 1 - o.hatchWeight0.y;
	}
	else if (hatchFactor > 2.0) {
		o.hatchWeight0.z = hatchFactor - 2.0;
		o.hatchWeight1.x = 1 - o.hatchWeight0.z;
	}
	else if (hatchFactor > 1.0) {
		o.hatchWeight1.x = hatchFactor - 1.0;
		o.hatchWeight1.y = 1 - o.hatchWeight1.x;
	}
	else {
		o.hatchWeight1.y = hatchFactor;
		o.hatchWeight1.z = 1 - o.hatchWeight1.y;
	}
	return o;
}
(4)实现片元着色器
fixed4 frag(v2f o) : SV_Target{
	//根据坐标,去纹理中采样,
	//具体哪些纹理可以采样,采样程度及混合由o.hatchWeight0 及 o.hatchWeight1 共计六个变量进行控制
	fixed4 HatchTex0 = tex2D(_Hatch0, o.uv) * o.hatchWeight0.x;
	fixed4 HatchTex1 = tex2D(_Hatch1, o.uv) * o.hatchWeight0.y;
	fixed4 HatchTex2 = tex2D(_Hatch2, o.uv) * o.hatchWeight0.z;
	fixed4 HatchTex3 = tex2D(_Hatch3, o.uv) * o.hatchWeight1.x;
	fixed4 HatchTex4 = tex2D(_Hatch4, o.uv) * o.hatchWeight1.y;
	fixed4 HatchTex5 = tex2D(_Hatch5, o.uv) * o.hatchWeight1.z;
	//计算纯白贡献度,最大范围中,o.hatchWeight0 及 o.hatchWeight16个分量均为0 ,不会采样任一纹理颜色
	//第二大范围中,只采样一张纹理,没有颜色混色,夹角越大,纯白贡献度越高,形成一种从白色到最浅素描采样的过度
	//后面5个范围,没有纯白贡献度,完全依赖纹理本身颜色
	fixed4 whiteColor = fixed4(1, 1, 1, 1) * (1 - o.hatchWeight0.x - o.hatchWeight0.y - 
					o.hatchWeight0.z - o.hatchWeight1.x - o.hatchWeight1.y - o.hatchWeight1.z);
	//最终颜色为这些颜色的和
	fixed4 hatchColor = HatchTex0 + HatchTex1 + HatchTex2 + HatchTex3 + HatchTex4 + HatchTex5 + whiteColor;
	//将得到的素描采样颜色与模型自身颜色进行混合
	return fixed4(hatchColor.rgb * _Color.rgb , 1.0);
			}
完整Shader代码
Shader "Custom/MyHatch"
{
	Properties{
		_Color("Main Color",COLOR) = (1,1,1,1)
		_TileFactor("TileFactor",Float) = 8
		_Outline("Outline", Range(0, 1)) = 0.1

		_Hatch0("Hatch0",2D) = "white" {}
		_Hatch1("Hatch1",2D) = "white" {}
		_Hatch2("Hatch2",2D) = "white" {}
		_Hatch3("Hatch3",2D) = "white" {}
		_Hatch4("Hatch4",2D) = "white" {}
		_Hatch5("Hatch5",2D) = "white" {}
	}

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

			#pragma fragment  frag
			#pragma vertex vert

			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			#include "UnityShaderVariables.cginc"


			fixed4 _Color;
			float _TileFactor;
			sampler2D _Hatch0;
			sampler2D _Hatch1;
			sampler2D _Hatch2;
			sampler2D _Hatch3;
			sampler2D _Hatch4;
			sampler2D _Hatch5;

			struct a2v {
				float4 vertex : POSITION;
				float4 normal : NORMAL;
				float2 texcoord : TEXCOORD0;
			};

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

			v2f vert(a2v i) {
				v2f o;
				o.pos = UnityObjectToClipPos(i.vertex);
				o.uv = i.texcoord.xy * _TileFactor;
				fixed3 worldLightDir = normalize(WorldSpaceLightDir(i.vertex));
				fixed3 worldNormal = UnityObjectToWorldNormal(i.normal);
				float diff = max(0, dot(worldLightDir , worldNormal));
				float hatchFactor = diff * 7.0;
				o.hatchWeight0 = fixed3(0, 0, 0);
				o.hatchWeight1 = fixed3(0, 0, 0);
				if (hatchFactor > 6.0) {
					//到达这个区间内的颜色为纯白

				}
				else if (hatchFactor > 5.0) {
					//最浅的那张颜色
					o.hatchWeight0.x = hatchFactor - 5.0;
				}
				else if (hatchFactor > 4.0) {
					o.hatchWeight0.x = hatchFactor - 4.0;
					o.hatchWeight0.y = 1 - o.hatchWeight0.x;
				}
				else if (hatchFactor > 3.0) {
					o.hatchWeight0.y = hatchFactor - 3.0;
					o.hatchWeight0.z = 1 - o.hatchWeight0.y;
				}
				else if (hatchFactor > 2.0) {
					o.hatchWeight0.z = hatchFactor - 2.0;
					o.hatchWeight1.x = 1 - o.hatchWeight0.z;
				}
				else if (hatchFactor > 1.0) {
					o.hatchWeight1.x = hatchFactor - 1.0;
					o.hatchWeight1.y = 1 - o.hatchWeight1.x;
				}
				else {
					o.hatchWeight1.y = hatchFactor;
					o.hatchWeight1.z = 1 - o.hatchWeight1.y;
				}

				return o;
			}

			fixed4 frag(v2f o) : SV_Target{
				fixed4 HatchTex0 = tex2D(_Hatch0, o.uv) * o.hatchWeight0.x;
				fixed4 HatchTex1 = tex2D(_Hatch1, o.uv) * o.hatchWeight0.y;
				fixed4 HatchTex2 = tex2D(_Hatch2, o.uv) * o.hatchWeight0.z;
				fixed4 HatchTex3 = tex2D(_Hatch3, o.uv) * o.hatchWeight1.x;
				fixed4 HatchTex4 = tex2D(_Hatch4, o.uv) * o.hatchWeight1.y;
				fixed4 HatchTex5 = tex2D(_Hatch5, o.uv) * o.hatchWeight1.z;
				fixed4 whiteColor = fixed4(1, 1, 1, 1) * (1 - o.hatchWeight0.x - o.hatchWeight0.y - 
					o.hatchWeight0.z - o.hatchWeight1.x - o.hatchWeight1.y - o.hatchWeight1.z);
				fixed4 hatchColor = HatchTex0 + HatchTex1 + HatchTex2 + HatchTex3 + HatchTex4 + HatchTex5 + whiteColor;

				return fixed4(hatchColor.rgb * _Color.rgb , 1.0);
			}
			ENDCG
		}
	}
}

总结
到这里,一个完整的素描采样就完成了,读者可以酌情按照需求加入一些描边,相应环境光强度等扩展
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值