shader blur2: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Verhalten an Rändern eingefügt, Array konstant gemacht.)
(Überarbeitung, besserer Shader, siehe Diskussion)
Zeile 7: Zeile 7:
 
|-
 
|-
 
|Post-Processing-Shader zum verwischen der Szene.
 
|Post-Processing-Shader zum verwischen der Szene.
|Skeptiker
+
|Skeptiker/Coolcat
|1.0
+
|1.1
 
|}
 
|}
  
Zeile 19: Zeile 19:
  
 
==Beschreibung==
 
==Beschreibung==
Dieser Shader verwischt die Szene.
+
[[Bild:Gaussian-function-plot.gif|framed|Gauss-Funktion mit [[Bild:sigma.gif]] = 3.2]]
 +
Dieser Shader verwischt die Szene mit Hilfe einer eindimensionalen Gauss-Funktion:
  
''textureWidth'' gibt die Breite der Textur an.<br>
+
[[Bild:Gaussian-function.gif]]
''textureHeight'' gibt die Höhe der Textur an.<br>
 
''horizontal'' true wenn horizontal verwischt werden soll, false wenn vertikal verwischt werden soll.<br><br>
 
''texture0'' muss in der Regel nicht explizit angegeben werden.
 
  
Der Shader wurde bewusst einfach gehalten, mit einigen kniffen kann man hier mit Sicherheit mehr Performance rausholen.
+
Dabei ist x die Entfernung vom Mittelpunkt und [[Bild:sigma.gif]] die gewünschte [http://de.wikipedia.org/wiki/Varianz Varianz]. Da die Gauss-Funktion relativ aufwendig ist, wurde sie bereits im Bereich x = -5...5 für [[Bild:sigma.gif]] = 3.2 ausgewertet. Die Funktionswerte wurden normalisiert um die Summe auf 1 zu bringen und dann als konstantes Array fest in den Shader eingebaut.
  
Da der Shader für jedes Texel der Textur die unmittelbaren nachbar Texel abtastet muss das Verhalten an den Rändern der Textur definiert werden, hier empfiehlt sich die Einstellung GL_CLAMP_TO_EDGE oder GL_CLAMP_TO_BORDER.
+
Um einen zweidimensionalen Blur-Effekt zu erreichen muss der Shader zweimal angewendet werden. Beispielsweise einmal horizontal und einmal vertikal. Dies ist zwar komplizierter aber im allgemeinen schneller, als eine zweidimsensionale Gauss-Funktion zu verwenden.
 +
 
 +
Über die uniform-Variable <tt>uShift</tt> kann gesteuert werden wie der Effekt angewendet wird:
 +
* <tt>uShift = vec2(1.0/textureWidth, 0.0)</tt> bewirkt einen horizontalen Filter
 +
* <tt>uShift = vec2(0.0, 1.0/textureHeight)</tt> bewirkt einen vertikalen Filter
 +
* <tt>uShift = vec2(1.0/textureWidth, 1.0/textureHeight)</tt> bewirkt einen diagonalen Filter
 +
 
 +
Um den Effekt zu verstärken gibt es verschiedene Möglichkeiten. Beispielsweise kann man etwas wie <tt>uShift = vec2(1.4/textureWidth, 0.0)</tt> versuchen. Dies vergrößert die Schrittweite und damit den Einzugsbereich des Shaders. Bei einem zu großen Faktor werden dabei jedoch nicht mehr ausreichend viele Texel der Textur betrachtet. In diesem Fall sollte man eine andere Gauss-Funktion mit größerem [[Bild:sigma.gif]] und einem weiteren Bereich verwenden. Dadurch steigt natürlich die Anzahl der Texturzugriffe pro Pixel, was eine Grafikkarte schnell ans Limit bringen kann. Aus diesem Grund kann es sinnvoll sein den Shader mehrfach anzuwenden, immer abwechselnd horizontal und vertikal. Dies hat den gleichen Effekt wie ein größeres [[Bild:sigma.gif]] und benötigt insgesamt weniger Texturzugriffe. Man muss einen Mittelweg aus Texturzugriffen und Renderpasses finden.
 +
 
 +
Da der Shader für jedes Texel der Textur die unmittelbaren Nachbartexel abtastet, muss das Verhalten an den Rändern der Textur definiert werden, hier empfiehlt sich die Einstellung GL_CLAMP_TO_EDGE oder GL_CLAMP_TO_BORDER.
  
 
==Besondere Vorraussetzungen==
 
==Besondere Vorraussetzungen==
Es handelt sich um einen Post-Processing-Shader, die Szene muss daher vorher in eine Textur gerendert werden. Anschließend muss diese Textur gebunden werden und als Fullscreen-Quad mit diesem Shader gerendert werden. Hier bietet sich ein FrameBufferObject an.
+
Es handelt sich um einen Post-Processing-Shader, die Szene muss daher vorher in eine Textur gerendert werden. Anschließend muss diese Textur gebunden werden und als Fullscreen-Quad mit diesem Shader gerendert werden. Hier bietet sich ein FrameBufferObject an, insbesondere da der Shader ja zweimal (oder noch öfter) ausgeführt werden muss.
 
 
  
 
==Code==
 
==Code==
Zeile 40: Zeile 46:
  
 
void main() {
 
void main() {
 
 
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
 
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
 
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
 
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
Zeile 50: Zeile 55:
 
#version 120
 
#version 120
  
uniform sampler2D texture0;
+
uniform sampler2D uTexture;
uniform int textureWidth;
+
uniform vec2 uShift;
uniform int textureHeight;
+
uniform bool horizontal;
+
const int gaussRadius = 11;
 
+
const float gaussFilter[gaussRadius] = float[gaussRadius](
const float gaussFilter[7] = float[7](
+
0.0402,0.0623,0.0877,0.1120,0.1297,0.1362,0.1297,0.1120,0.0877,0.0623,0.0402
0.015625,
 
0.09375,
 
0.234375,
 
0.3125,
 
0.234375,
 
0.09375,
 
0.015625
 
 
);
 
);
 
+
 
void main() {
 
void main() {
 
+
vec2 texCoord = gl_TexCoord[0] - float(int(gaussRadius/2)) * uShift;
vec4 color = vec4(0.0, 0.0, 0.0, 0.0);
+
vec3 color = vec3(0.0, 0.0, 0.0);  
+
for (int i=0; i<gaussRadius; ++i) {  
for (int i = 0; i < 7; i++) {
+
color += gaussFilter[i] * texture2D(uTexture, texCoord).xyz;
+
texCoord += uShift;
vec2 texCoord;
 
 
if (horizontal) {
 
 
texCoord = vec2(gl_TexCoord[0].x + float(i - 3) / float(textureWidth), gl_TexCoord[0].y);
 
} else {
 
 
texCoord = vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + float(i - 3) / float(textureHeight));
 
}
 
 
color += texture2D(texture0, texCoord) * gaussFilter[i];
 
 
}
 
}
 
+
gl_FragColor = vec4(color,1.0);
gl_FragColor = color;
 
 
}
 
}
 
</source>
 
</source>

Version vom 24. Dezember 2009, 15:55 Uhr

Blur

Zurück zur Shadersammlung

Beschreibung Autor Version
Post-Processing-Shader zum verwischen der Szene. Skeptiker/Coolcat 1.1

Bilder

Vorher
Nachher

Beschreibung

Gauss-Funktion mit sigma.gif = 3.2

Dieser Shader verwischt die Szene mit Hilfe einer eindimensionalen Gauss-Funktion:

Gaussian-function.gif

Dabei ist x die Entfernung vom Mittelpunkt und sigma.gif die gewünschte Varianz. Da die Gauss-Funktion relativ aufwendig ist, wurde sie bereits im Bereich x = -5...5 für sigma.gif = 3.2 ausgewertet. Die Funktionswerte wurden normalisiert um die Summe auf 1 zu bringen und dann als konstantes Array fest in den Shader eingebaut.

Um einen zweidimensionalen Blur-Effekt zu erreichen muss der Shader zweimal angewendet werden. Beispielsweise einmal horizontal und einmal vertikal. Dies ist zwar komplizierter aber im allgemeinen schneller, als eine zweidimsensionale Gauss-Funktion zu verwenden.

Über die uniform-Variable uShift kann gesteuert werden wie der Effekt angewendet wird:

  • uShift = vec2(1.0/textureWidth, 0.0) bewirkt einen horizontalen Filter
  • uShift = vec2(0.0, 1.0/textureHeight) bewirkt einen vertikalen Filter
  • uShift = vec2(1.0/textureWidth, 1.0/textureHeight) bewirkt einen diagonalen Filter

Um den Effekt zu verstärken gibt es verschiedene Möglichkeiten. Beispielsweise kann man etwas wie uShift = vec2(1.4/textureWidth, 0.0) versuchen. Dies vergrößert die Schrittweite und damit den Einzugsbereich des Shaders. Bei einem zu großen Faktor werden dabei jedoch nicht mehr ausreichend viele Texel der Textur betrachtet. In diesem Fall sollte man eine andere Gauss-Funktion mit größerem sigma.gif und einem weiteren Bereich verwenden. Dadurch steigt natürlich die Anzahl der Texturzugriffe pro Pixel, was eine Grafikkarte schnell ans Limit bringen kann. Aus diesem Grund kann es sinnvoll sein den Shader mehrfach anzuwenden, immer abwechselnd horizontal und vertikal. Dies hat den gleichen Effekt wie ein größeres sigma.gif und benötigt insgesamt weniger Texturzugriffe. Man muss einen Mittelweg aus Texturzugriffen und Renderpasses finden.

Da der Shader für jedes Texel der Textur die unmittelbaren Nachbartexel abtastet, muss das Verhalten an den Rändern der Textur definiert werden, hier empfiehlt sich die Einstellung GL_CLAMP_TO_EDGE oder GL_CLAMP_TO_BORDER.

Besondere Vorraussetzungen

Es handelt sich um einen Post-Processing-Shader, die Szene muss daher vorher in eine Textur gerendert werden. Anschließend muss diese Textur gebunden werden und als Fullscreen-Quad mit diesem Shader gerendert werden. Hier bietet sich ein FrameBufferObject an, insbesondere da der Shader ja zweimal (oder noch öfter) ausgeführt werden muss.

Code

Vertex Shader

#version 120

void main() {
	gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
	gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}

Fragment Shader

#version 120

uniform sampler2D uTexture;
uniform vec2 uShift;
 
const int gaussRadius = 11;
const float gaussFilter[gaussRadius] = float[gaussRadius](
	0.0402,0.0623,0.0877,0.1120,0.1297,0.1362,0.1297,0.1120,0.0877,0.0623,0.0402
);
 
void main() {
	vec2 texCoord = gl_TexCoord[0] - float(int(gaussRadius/2)) * uShift;
	vec3 color = vec3(0.0, 0.0, 0.0); 
	for (int i=0; i<gaussRadius; ++i) { 
		color += gaussFilter[i] * texture2D(uTexture, texCoord).xyz;
		texCoord += uShift;
	}
	gl_FragColor = vec4(color,1.0);
}