0%

简单水墨风格Shader

效果图

image-20220414191627658.png

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 需要一个MainTex主纹理贴图、一个水墨笔刷贴图

Shader "Unlit/shuimo"
{
Properties
{
_MainTex("Main", 2D) = "white" {}
_Thred("Edge Thred" , Range(0.01,1)) = 0.25
_Range("Edge Range" , Range(0,5)) = 1
_Pow("Edge Intensity",Range(0,5))=1
_BrushTex("Brush Texture", 2D) = "white" {}
_Saturation("Saturation" ,Range(0,1)) = 0
_BlenderRow("BlenderRow",Range(0,1)) = 0

[Enum(Opacity,1,Darken,2,Lighten,3,Multiply,4,Screen,5,Overlay,6,SoftLight,7)]
_BlendType("Blend Type", Int) = 1
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"

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

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

sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BrushTex;
float _BlendType;
float _Thred;
float _Range;
float _Pow;
float _Saturation;
float _BlenderRow;

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

fixed4 frag (v2f i) : SV_Target
{
float3 ViewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
float NdotV = max( dot(ViewDir,i.worldNormal) , 0);
fixed4 mainTex = tex2D(_MainTex, i.uv);
fixed4 brushTex = tex2D(_BrushTex, i.uv);

// fixed texGrey = (mainTex.r + mainTex.g + mainTex.b)*0.33; // 平均值法求灰度
// GrayScale=0.299*R+0.578*G+0.114*B 加权平均求灰度
fixed texGrey = 0.299 * mainTex.r + 0.578 * mainTex.g + 0.114 * mainTex.b;
texGrey = pow(texGrey, 0.3);
texGrey *= 1 - cos(texGrey * 3.14);
fixed brushGrey = (brushTex.r + brushTex.g + brushTex.b)*0.33;

fixed blend = texGrey * 0.5 + brushGrey * 0.5;
fixed4 col = fixed4(blend, blend, blend, 1);

fixed edge = pow(NdotV, 1) / _Range;
edge = edge > _Thred ? 1 : edge;
edge = pow(edge, _Pow);
fixed4 edgeColor = fixed4(edge, edge, edge, edge);

col = edgeColor * (1 - edgeColor.a) + col * (edgeColor.a);

fixed4 rowtex =tex2D(_MainTex,i.uv);
//saturation饱和度:首先根据公式计算同等亮度情况下饱和度最低的值:
fixed minGray = 0.2125 * rowtex.r + 0.7154 * rowtex.g + 0.0721 * rowtex.b;
fixed3 mingrayColor = fixed3(minGray, minGray, minGray);
//根据Saturation在饱和度最低的图像和原图之间差值
float3 RowColor = lerp(mingrayColor, rowtex.rgb, _Saturation);

float3 blendrow = RowColor * (1 - _BlenderRow) + float3(brushGrey,brushGrey,brushGrey) * _BlenderRow;
float3 finalColor = col * blendrow;

return fixed4(finalColor,1);
}
ENDCG
}
}
}

实现方法
一、求灰度图

对灰度图像的处理一般有以下四种方法:

1.分量法

该方法将彩色图像中的三分量的亮度作为三个灰度图像的灰度值,可根据应用需要选取一种灰度图像。即:可以选取RGB中的任一颜色通道作为灰度值。公式为:GrayScale1 = R,GrayScale2 = G,GrayScale3 = B

2.最大值法

该方法将彩色图像中的三分量亮度的最大值作为灰度图的灰度值,一般的,该方法求取后的灰度图亮度最高。公式为:GrayScale = max(R, G, B)

3.平均值法

该方法将彩色图像中的RGB三分量的颜色值来求取平均值作为灰度值。公式为:GrayScale = (R+G+B) / 3

4.加权平均法

该方法根据重要性及其它指标,将三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感最高,对蓝色敏感最低,因此,按下式对RGB三分量进行加权平均能得到相对合理的灰度图像。公式为:GrayScale = 0.299 R + 0.578 G + 0.114 *B

此处对主纹理采用加权平均法、对水墨笔刷采用平均值法

二、利用混合公式混合颜色Texture和水墨笔触Texture

图片混合的多种模式:https://www.cnblogs.com/kex1n/p/3663533.html

三、利用 VdotN 求边缘效果
1
2
3
4
fixed edge = pow(NdotV, 1) / _Range;
edge = edge > _Thred ? 1 : edge;
edge = pow(edge, _Pow);
fixed4 edgeColor = fixed4(edge, edge, edge, edge);
四、降低原图饱和度,再将其与水墨笔刷进行混合 (可选项)
1
2
3
4
5
6
7
fixed4 rowtex =tex2D(_MainTex,i.uv);
//saturation饱和度:首先根据公式计算同等亮度情况下饱和度最低的值:
fixed minGray = 0.2125 * rowtex.r + 0.7154 * rowtex.g + 0.0721 * rowtex.b;
fixed3 mingrayColor = fixed3(minGray, minGray, minGray);
//根据Saturation在饱和度最低的图像和原图之间差值
float3 RowColor = lerp(mingrayColor, rowtex.rgb, _Saturation);
float3 blendrow = RowColor * (1 - _BlenderRow) + float3(brushGrey,brushGrey,brushGrey) * _BlenderRow;