文章转载于:https://r3dux.org/2011/06/glsl-image-processing/
Image Processing Vertex Shader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#version 330
// Read-only uniform values shared across all vertexes
uniform
mat4
mvpMatrix
;
// Incoming per-vertex values
in
vec4
vVertex
;
in
vec2
vTexCoord0
;
// Outgoing interpolated values
smooth
out
vec2
vTex
;
void
main
(
void
)
{
// Pass through the texture coordinates
vTex
=
vTexCoord0
;
// Transform the geometry
gl_Position
=
mvpMatrix
*
vVertex
;
}
|
Image Processing Fragment Shader
1
2
3
4
5
6
7
8
9
10
11
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
|
#version 330
uniform
sampler2D
quadTexture
;
uniform
int
filterNumber
;
uniform
vec2
tcOffset
[
25
]
;
// Texture coordinate offsets
in
vec2
vTex
;
out
vec4
vFragColour
;
void
main
(
void
)
{
// Standard
if
(
filterNumber
==
0
)
{
vFragColour
=
texture2D
(
quadTexture
,
vTex
)
;
}
// Greyscale
if
(
filterNumber
==
1
)
{
// Convert to greyscale using NTSC weightings
float
grey
=
dot
(
texture2D
(
quadTexture
,
vTex
)
.
rgb
,
vec3
(
0.299
,
0.587
,
0.114
)
)
;
vFragColour
=
vec4
(
grey
,
grey
,
grey
,
1.0
)
;
}
// Sepia tone
if
(
filterNumber
==
2
)
{
// Convert to greyscale using NTSC weightings
float
grey
=
dot
(
texture2D
(
quadTexture
,
vTex
)
.
rgb
,
vec3
(
0.299
,
0.587
,
0.114
)
)
;
// Play with these rgb weightings to get different tones.
// (As long as all rgb weightings add up to 1.0 you won't lighten or darken the image)
vFragColour
=
vec4
(
grey
*
vec3
(
1.2
,
1.0
,
0.8
)
,
1.0
)
;
}
// Negative
if
(
filterNumber
==
3
)
{
vec4
texMapColour
=
texture2D
(
quadTexture
,
vTex
)
;
vFragColour
=
vec4
(
1.0
-
texMapColour
.
rgb
,
1.0
)
;
}
// Blur (gaussian)
if
(
filterNumber
==
4
)
{
vec4
sample
[
25
]
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
// Sample a grid around and including our texel
sample
[
i
]
=
texture
(
quadTexture
,
vTex
+
tcOffset
[
i
]
)
;
}
// Gaussian weighting:
// 1 4 7 4 1
// 4 16 26 16 4
// 7 26 41 26 7 / 273 (i.e. divide by total of weightings)
// 4 16 26 16 4
// 1 4 7 4 1
vFragColour
=
(
(
1.0
*
(
sample
[
0
]
+
sample
[
4
]
+
sample
[
20
]
+
sample
[
24
]
)
)
+
(
4.0
*
(
sample
[
1
]
+
sample
[
3
]
+
sample
[
5
]
+
sample
[
9
]
+
sample
[
15
]
+
sample
[
19
]
+
sample
[
21
]
+
sample
[
23
]
)
)
+
(
7.0
*
(
sample
[
2
]
+
sample
[
10
]
+
sample
[
14
]
+
sample
[
22
]
)
)
+
(
16.0
*
(
sample
[
6
]
+
sample
[
8
]
+
sample
[
16
]
+
sample
[
18
]
)
)
+
(
26.0
*
(
sample
[
7
]
+
sample
[
11
]
+
sample
[
13
]
+
sample
[
17
]
)
)
+
(
41.0
*
sample
[
12
]
)
)
/
273.0
;
}
// Blur (mean filter)
if
(
filterNumber
==
5
)
{
vFragColour
=
vec4
(
0.0
)
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
// Sample a grid around and including our texel
vFragColour
+=
texture
(
quadTexture
,
vTex
+
tcOffset
[
i
]
)
;
}
// Divide by the number of samples to get our mean
vFragColour
/=
25
;
}
// Sharpen
if
(
filterNumber
==
6
)
{
vec4
sample
[
25
]
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
// Sample a grid around and including our texel
sample
[
i
]
=
texture
(
quadTexture
,
vTex
+
tcOffset
[
i
]
)
;
}
// Sharpen weighting:
// -1 -1 -1 -1 -1
// -1 -1 -1 -1 -1
// -1 -1 25 -1 -1
// -1 -1 -1 -1 -1
// -1 -1 -1 -1 -1
vFragColour
=
25.0
*
sample
[
12
]
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
if
(
i
!=
12
)
vFragColour
-=
sample
[
i
]
;
}
}
// Dilate
if
(
filterNumber
==
7
)
{
vec4
sample
[
25
]
;
vec4
maxValue
=
vec4
(
0.0
)
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
// Sample a grid around and including our texel
sample
[
i
]
=
texture
(
quadTexture
,
vTex
+
tcOffset
[
i
]
)
;
// Keep the maximum value
maxValue
=
max
(
sample
[
i
]
,
maxValue
)
;
}
vFragColour
=
maxValue
;
}
// Erode
if
(
filterNumber
==
8
)
{
vec4
sample
[
25
]
;
vec4
minValue
=
vec4
(
1.0
)
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
// Sample a grid around and including our texel
sample
[
i
]
=
texture
(
quadTexture
,
vTex
+
tcOffset
[
i
]
)
;
// Keep the minimum value
minValue
=
min
(
sample
[
i
]
,
minValue
)
;
}
vFragColour
=
minValue
;
}
// Laplacian Edge Detection (very, very similar to sharpen filter - check it out!)
if
(
filterNumber
==
9
)
{
vec4
sample
[
25
]
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
// Sample a grid around and including our texel
sample
[
i
]
=
texture
(
quadTexture
,
vTex
+
tcOffset
[
i
]
)
;
}
// Laplacian weighting:
// -1 -1 -1 -1 -1
// -1 -1 -1 -1 -1
// -1 -1 24 -1 -1
// -1 -1 -1 -1 -1
// -1 -1 -1 -1 -1
vFragColour
=
24.0
*
sample
[
12
]
;
for
(
int
i
=
0
;
i
<
25
;
i
++
)
{
if
(
i
!=
12
)
vFragColour
-=
sample
[
i
]
;
}
}
}
|
The only other bit of code worth mentioning is the generation of the texture coordinate offsets, which is accomplished with the following C++ code (yes, it’s kinda ugly – no, I didn’t write it – yes, it works):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
// Set up texture sampling offset storage
const
GLint
tcOffsetColumns
=
5
;
const
GLint
tcOffsetRows
=
5
;
GLfloat
texCoordOffsets
[
tcOffsetColumns
*
tcOffsetRows
*
2
]
;
// Calculate texture coordinate offsets for kernel convolution effects
void
genTexCoordOffsets
(
GLuint
width
,
GLuint
height
,
GLfloat
step
=
1.0f
)
// Note: Change this step value to increase the number of pixels we sample across...
{
// Note: You can multiply the step to displace the samples further. Do this with diff values horiz and vert and you have directional blur of a sort...
float
xInc
=
step
/
(
GLfloat
)
(
windowWidth
)
;
float
yInc
=
step
/
(
GLfloat
)
(
windowHeight
)
;
for
(
int
i
=
0
;
i
<
tcOffsetColumns
;
i
++
)
{
for
(
int
j
=
0
;
j
<
tcOffsetRows
;
j
++
)
{
texCoordOffsets
[
(
(
(
i
*
5
)
+
j
)
*
2
)
+
0
]
=
(
-
2.0f
*
xInc
)
+
(
(
GLfloat
)
i
*
xInc
)
;
texCoordOffsets
[
(
(
(
i
*
5
)
+
j
)
*
2
)
+
1
]
=
(
-
2.0f
*
yInc
)
+
(
(
GLfloat
)
j
*
yInc
)
;
}
}
}
|
You can then draw your full-screen quad something like this:
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
|
// *** Draw the original geometry to a FBO here ***
// Now draw our full-screen quad
projectionMatrix
.
PushMatrix
(
)
;
projectionMatrix
.
LoadIdentity
(
)
;
projectionMatrix
.
LoadMatrix
(
orthoMatrix
)
;
modelViewMatrix
.
PushMatrix
(
)
;
modelViewMatrix
.
LoadIdentity
(
glDisable
(
GL_DEPTH_TEST
)
;
glActiveTexture
(
GL_TEXTURE0
)
;
glBindTexture
(
GL_TEXTURE_2D
,
fboTexture
)
;
// Select the texture in our framebuffer
glUseProgram
(
r3dTextureProgram
)
;
glUniformMatrix4fv
(
locTexMVPMatrix
,
1
,
GL_FALSE
,
transformPipeline
.
GetModelViewProjectionMatrix
(
)
)
;
glUniform1i
(
locTexTextureMap
,
0
)
;
glUniform1i
(
locTexFilterNumber
,
filterNumber
)
;
glUniform2fv
(
locTexTCOffsets
,
25
,
texCoordOffsets
)
;
// Pass in 25 vec2s in our texture coordinate offset array
screenQuad
.
Draw
(
)
;
glEnable
(
GL_DEPTH_TEST
)
;
modelViewMatrix
.
PopMatrix
(
)
;
projectionMatrix
.
PopMatrix
(
)
;
|
Job’s a good ‘un.