服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Unity Shader实现水墨效果

Unity Shader实现水墨效果

2022-09-06 11:20ZzEeRO C#

这篇文章主要为大家详细介绍了Unity Shader实现水墨效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

unity shader学习:水墨效果

偶然在网上看到9级铁甲蛹大神的水墨风格后处理觉得挺有意思,参照着实现一下,还是涉及到之前油画效果的算法,叫什么滤波暂时不清楚,应该用来处理手绘效果挺多的。

水墨风格基本原理:高斯模糊原始图像,用深度算出边缘进行描边,最后用画笔效果的滤波完成最终图像。

Unity Shader实现水墨效果

Unity Shader实现水墨效果

有需要可以用post proces改变颜色范围,更接近水墨的颜色。

Unity Shader实现水墨效果

Unity Shader实现水墨效果

c#部分:

?
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
//屏幕后处理基类
using unityengine;
using system.collections;
 
//非运行时也触发效果
[executeineditmode]
//屏幕后处理特效一般都需要绑定在摄像机上
[requirecomponent(typeof(camera))]
//提供一个后处理的基类,主要功能在于直接通过inspector面板拖入shader,生成shader对应的材质
public class posteffectbase : monobehaviour
{
 
 //inspector面板上直接拖入
 public shader shader = null;
 private material _material = null;
 public material _material
 {
  get
  {
   if (_material == null)
    _material = generatematerial(shader);
   return _material;
  }
 }
 
 //根据shader创建用于屏幕特效的材质
 protected material generatematerial(shader shader)
 {
  if (shader == null)
   return null;
  //需要判断shader是否支持
  if (shader.issupported == false)
   return null;
  material material = new material(shader);
  material.hideflags = hideflags.dontsave;
  if (material)
   return material;
  return null;
 }
 
}
?
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
//挂在摄像机上
using system.collections;
using system.collections.generic;
using unityengine;
 
[executeineditmode]
public class chineseinkposteffect : posteffectbase {
 /// <summary>
 /// 降分辨率未操作
 /// </summary>
 [range(0,5)]
 public int downsample = 1;
 /// <summary>
 /// 高斯模糊采样缩放系数
 /// </summary>
 [range(0,5)]
 public int samplerscale = 1;
 /// <summary>
 /// 高斯模糊迭代次数
 /// </summary>
 [range(0,10)]
 public int count = 1;
 /// <summary>
 /// 边缘宽度
 /// </summary>
 [range(0.0f,10.0f)]
 public float edgewidth = 3.0f;
 /// <summary>
 /// 边缘最小宽度
 /// </summary>
 [range(0.0f,1.0f)]
 public float sensitive = 0.35f;
 /// <summary>
 /// 画笔滤波系数
 /// </summary>
 [range(0,10)]
 public int paintfactor = 4;
 /// <summary>
 /// 噪声图
 /// </summary>
 public texture noisetexture;
 private camera cam;
 private void start()
 {
  cam = getcomponent<camera>();
  //开启深度法线图
  cam.depthtexturemode = depthtexturemode.depthnormals;
 }
 private void onrenderimage(rendertexture source, rendertexture destination)
 {
  if (_material)
  {
   rendertexture temp1 = rendertexture.gettemporary(source.width >> downsample, source.height >> downsample, 0, source.format);
   rendertexture temp2 = rendertexture.gettemporary(source.width >> downsample, source.height >> downsample, 0, source.format);
 
   graphics.blit(source, temp1);
   for (int i = 0; i < count; i++)
   {
    //高斯模糊横向纵向两次(pass0)
    _material.setvector("_offsets", new vector4(0, samplerscale, 0, 0));
    graphics.blit(temp1, temp2, _material, 0);
    _material.setvector("_offsets", new vector4(samplerscale, 0, 0, 0));
    graphics.blit(temp2, temp1, _material, 0);
   }
 
   //描边(pass1)
   _material.settexture("_blurtex", temp1);
   _material.settexture("_noisetex", noisetexture);
   _material.setfloat("_edgewidth", edgewidth);
   _material.setfloat("_sensitive", sensitive);
   graphics.blit(temp1, temp2,_material,1);
 
   //画笔滤波(pass2)
   _material.settexture("_painttex", temp2);
   _material.setint("_paintfactor", paintfactor);
   graphics.blit(temp2, destination, _material, 2);
 
   rendertexture.releasetemporary(temp1);
   rendertexture.releasetemporary(temp2);
  }
 }
}

shader部分:

?
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
shader "unlit/chineseink"
{
 properties
 {
 //原始画面
 _maintex ("texture", 2d) = "white" {}
  //高斯模糊画面
  _blurtex("blur",2d) = "white"{}
 //水墨画面
 _painttex("painttex",2d)="white"{}
 }
 cginclude
 #include "unitycg.cginc"
 //深度法线图
 sampler2d _cameradepthnormalstexture;
 sampler2d _maintex;
 sampler2d _blurtex;
 sampler2d _painttex;
 sampler2d _noisetex;
 float4 _blurtex_texelsize;
 float4 _maintex_st;
 float4 _maintex_texelsize;
 float4 _painttex_texelsize;
 float4 _offsets;
 float _edgewidth;
 float _sensitive;
 int _paintfactor;
 
 //取灰度
 float luminance(fixed3 color) {
 return 0.2125*color.r + 0.7154*color.g + 0.0721*color.b;
 }
 //高斯模糊部分
 struct v2f_blur
 {
 float2 uv : texcoord0;
 float4 vertex : sv_position;
 float4 uv01:texcoord1;
 float4 uv23:texcoord2;
 float4 uv45:texcoord3;
 };
 
 v2f_blur vert_blur(appdata_img v)
 {
 v2f_blur o;
 o.vertex = unityobjecttoclippos(v.vertex);
 o.uv = v.texcoord.xy;
 _offsets *= _maintex_texelsize.xyxy;
 o.uv01 = v.texcoord.xyxy + _offsets.xyxy*float4(1, 1, -1, -1);
 o.uv23 = v.texcoord.xyxy + _offsets.xyxy*float4(1, 1, -1, -1)*2.0;
 o.uv45 = v.texcoord.xyxy + _offsets.xyxy*float4(1, 1, -1, -1)*3.0;
 return o;
 }
 
 float4 frag_blur(v2f_blur i) : sv_target
 {
 float4 color = float4(0,0,0,0);
 color += 0.40*tex2d(_maintex, i.uv);
 color += 0.15*tex2d(_maintex, i.uv01.xy);
 color += 0.15*tex2d(_maintex, i.uv01.zw);
 color += 0.10*tex2d(_maintex, i.uv23.xy);
 color += 0.10*tex2d(_maintex, i.uv23.zw);
 color += 0.05*tex2d(_maintex, i.uv45.xy);
 color += 0.05*tex2d(_maintex, i.uv45.zw);
 return color;
 }
 //边缘检测部分
 struct v2f_edge{
 float2 uv:texcoord0;
 float4 vertex:sv_position;
 };
 
 v2f_edge vert_edge(appdata_img v){
 v2f_edge o;
 o.vertex = unityobjecttoclippos(v.vertex);
 o.uv = v.texcoord;
 return o;
 }
 
 float4 frag_edge(v2f_edge i):sv_target{
 //噪声
 float n = tex2d(_noisetex,i.uv).r;
 float3 col0 = tex2d(_cameradepthnormalstexture, i.uv + _edgewidth * _blurtex_texelsize.xy*float2(1,1)).xyz;
 float3 col1 = tex2d(_cameradepthnormalstexture, i.uv + _edgewidth * _blurtex_texelsize.xy*float2(1,-1)).xyz;
 float3 col2 = tex2d(_cameradepthnormalstexture, i.uv + _edgewidth * _blurtex_texelsize.xy*float2(-1, 1)).xyz;
 float3 col3 = tex2d(_cameradepthnormalstexture, i.uv + _edgewidth * _blurtex_texelsize.xy*float2(-1,-1)).xyz;
 float edge = luminance(pow(col0 - col3, 2) + pow(col1 - col2, 2));
 edge = pow(edge, 0.2);
 if (edge<_sensitive)
 {
 edge = 0;
 }
 else
 {
 edge = n;
 }
 float3 color = tex2d(_blurtex, i.uv);
 float3 finalcolor = edge * float3(0, 0, 0) + (1 - edge)*color*(0.95+0.1*n);
 return float4(finalcolor, 1.0);
 }
 //画笔滤波部分
 struct v2f_paint {
 float2 uv:texcoord0;
 float4 vertex:sv_position;
 };
 
 v2f_paint vert_paint(appdata_img v) {
 v2f_paint o;
 o.uv = v.texcoord;
 o.vertex = unityobjecttoclippos(v.vertex);
 return o;
 }
 
 float4 frag_paint(v2f_paint i):sv_target{
 float3 m0 = 0.0;
 float3 m1 = 0.0;
 float3 s0 = 0.0;
 float3 s1 = 0.0;
 float3 c = 0.0;
 int radius = _paintfactor;
 int r = (radius + 1)*(radius + 1);
 for (int j = -radius; j <= 0; ++j)
 {
 for (int k = -radius; k <= 0; ++k)
 {
 c = tex2d(_painttex, i.uv + _painttex_texelsize.xy * float2(k, j)).xyz;
 m0 += c;
 s0 += c * c;
 }
 }
  for (int j = 0; j <= radius; ++j)
  {
  for (int k = 0; k <= radius; ++k)
  {
  c = tex2d(_painttex, i.uv + _painttex_texelsize.xy * float2(k, j)).xyz;
  m1 += c;
  s1 += c * c;
  }
  }
  float4 finalfragcolor = 0.;
  float min_sigma2 = 1e+2;
  m0 /= r;
  s0 = abs(s0 / r - m0 * m0);
  float sigma2 = s0.r + s0.g + s0.b;
  if (sigma2 < min_sigma2)
  {
  min_sigma2 = sigma2;
  finalfragcolor = float4(m0, 1.0);
  }
  m1 /= r;
  s1 = abs(s1 / r - m1 * m1);
  sigma2 = s1.r + s1.g + s1.b;
  if (sigma2 < min_sigma2)
  {
  min_sigma2 = sigma2;
  finalfragcolor = float4(m1, 1.0);
  }
 return finalfragcolor;
 }
 
 endcg
 
 subshader
 {
 pass
 {
 cgprogram
   #pragma vertex vert_blur
   #pragma fragment frag_blur
 endcg
 }
 
 pass
 {
 cgprogram
   #pragma vertex vert_edge
   #pragma fragment frag_edge
 endcg
 }
 
 pass
 {
 cgprogram
   #pragma vertex vert_paint
   #pragma fragment frag_paint
 endcg
 }
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/qq_36107199/article/details/86507737

延伸 · 阅读

精彩推荐
  • C#分享我在工作中遇到的多线程下导致RCW无法释放的问题

    分享我在工作中遇到的多线程下导致RCW无法释放的问题

    最近在做项目中遇到一个问题,在调用一个类库中的方法时,出现如下异常信息:尝试释放正在使用的RCW,活动线程或其他线程上正在使用该 RCW,释放正在...

    眼神与背影5892021-11-05
  • C#C#.NET中如何批量插入大量数据到数据库中

    C#.NET中如何批量插入大量数据到数据库中

    这篇文章主要给大家介绍C#.net中如何批量插入大量数据到数据库中,本文涉及到C#.net中批量插入数据到数据库中方面的内容,对C#.net批量插入数据到数据库...

    C#教程网4152021-11-01
  • C#asp.net(c#)编程实现将彩色图片变灰阶图片的方法示例

    asp.net(c#)编程实现将彩色图片变灰阶图片的方法示例

    这篇文章主要介绍了asp.net(c#)编程实现将彩色图片变灰阶图片的方法,结合实例形式分析了C#图片读取及属性操作相关技巧,需要的朋友可以参考下...

    roucheng7032022-01-19
  • C#C#实现写系统日志的方法

    C#实现写系统日志的方法

    这篇文章主要介绍了C#实现写系统日志的方法,涉及C#针对系统日志的创建、写入及删除等技巧,非常具有实用价值,需要的朋友可以参考下...

    我心依旧9092021-10-22
  • C#C#实现类型的比较示例详解

    C#实现类型的比较示例详解

    这篇文章主要给大家介绍了关于C#实现类型的比较的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C#具有一定的参考学习价值,需要的...

    solenovex6062022-07-21
  • C#C# WebApi 接口传参详解

    C# WebApi 接口传参详解

    这篇文章主要介绍了C# WebApi 接口传参详解,本篇打算通过get、post、put、delete四种请求方式分别谈谈基础类型(包括int/string/datetime等)、实体、数组等类型...

    懒得安分9872022-02-25
  • C#WPF TextBox和PasswordBox添加水印

    WPF TextBox和PasswordBox添加水印

    这篇文章主要为大家详细介绍了WPF TextBox和PasswordBox添加水印的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    眾尋11572021-12-09
  • C#C#实现绑定DataGridView与TextBox之间关联的方法

    C#实现绑定DataGridView与TextBox之间关联的方法

    这篇文章主要介绍了C#实现绑定DataGridView与TextBox之间关联的方法,涉及C#绑定控件关联性的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...

    我心依旧6212021-10-20