NPR油画风格着色器

oilpaint-rendeirng

非真实感风格化处理通常是降低像素的精度,减少细节或者以特殊的纹理进行覆盖。油画风格的特点为块状的笔触与色块,将画面色块化的方式一种是降低图像的色阶,但是使用这种方式会减少画面颜色的数量,与原图有较大的偏差的丢失。而使用类似聚类算法,将临近像素的颜色进行合并和混合可以达到较好的效果。

整体的计算流程是,对于一个像素,计算它相邻的10 * 10范围内的所有像素,取每个像素的RGB值之和取均值。将这些均值映射到20个梯度至内,每个像素落到一个梯度值就累加上像素颜色的值,同时梯度的索引加一。当遍历完所有的像素之后,选取索引次数最高的梯度的所有像素的平均值,作为该像素的颜色。

Unity ShaderLab中实现的具体代码如下:

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

Shader "Yemi/OilpaintShader" {
Properties {
_MainTex ("Albedo (RGB)", 2D) = "white" {}

}
SubShader {
Pass
{
Name "Pass1"
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#include "UnityCG.cginc"
uniform sampler2D _MainTex;

half4 frag(v2f_img i) : COLOR
{
int a[20] = { 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0, 0,0,0,0,0 };
half4 c[20] = {
half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),
half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),
half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),
half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),half4(0,0,0,0),

};
for (int x = -5; x <= 5; x++)
{

for (int y = -5; y <= 5; y++)
{

half4 p1 = tex2D(_MainTex, i.uv + 0.001*half2(x,y));
int pi1 = int((p1.r + p1.g + p1.b)/3.0 * 19);
a[pi1] ++;
c[pi1] += p1;
}
}
int index = 0;
int max = a[0];
for (int n = 0; n < 20; n++)
{
if (a[n] >= max)
{
max = a[n];
index = n;
}
}
half4 oc = c[index] / max;
oc.a = 1;
return oc;
}
ENDCG
}
}
FallBack "Diffuse"
}

由于每个像素要对贴图进行多达100次的采样和大量的计算该shader的耗时太长,不能实时运行。可以考虑将Framebuffer downsample之后进行采样,同时再加入随机值减少贴图采样的次数。