OpenGL 屏幕后处理效果
OpenGL screen postprocessing effects
我在 Java 中使用 OpenGL 构建了一个不错的音乐可视化工具。它看起来已经很整洁了,但我考虑过向它添加一些 post 处理。当时,它看起来像这样:
已经有一个用于记录输出的帧缓冲区,所以我有可用的纹理。现在我想知道是否有人对某些效果有想法。当前的片段着色器如下所示:
#version 440
in vec3 position_FS_in;
in vec2 texCoords_FS_in;
out vec4 out_Color;
//the texture of the last Frame by now exactly the same as the output
uniform sampler2D textureSampler;
//available data:
//the average height of the lines seen in the screenshot, ranging from 0 to 1
uniform float mean;
//the array of heights of the lines seen in the screenshot
uniform float music[512];
void main()
{
vec4 texColor = texture(textureSampler, texCoords_FS_in);
//insert post processing here
out_Color = texColor;
}
大多数 post 处理效果随时间变化,因此随时间变化的制服很常见。例如,可以通过使用 sin(elapsedSec * wavyRadsPerSec + (PI * gl_FragCoord.y * 0.5 + 0.5) * wavyCyclesInFrame)
.
偏移纹理坐标来创建 "wavy" 效果
一些 "postprocessing" 效果可以非常简单地完成,例如,您可以在整个屏幕上混合一个近乎黑色的透明四边形,而不是使用 glClear
清除后台缓冲区。这将创建一个持久性效果,过去的帧在当前帧后面逐渐变黑。
可以通过在距每个点的不同距离处获取多个样本,并对较近的样本进行更大的加权并求和来实现定向模糊。如果你跟踪一个点相对于相机位置和方向的运动,它可以做成运动模糊实现。
颜色变换也非常简单,只需将RGB 视为向量的XYZ,然后对其进行有趣的变换即可。棕褐色和 "psychedelic" 颜色可以通过这种方式产生。
您可能会发现将颜色转换为类似 HSV 的颜色、对该表示进行转换并将其转换回 RGB 以用于帧缓冲区写入很有帮助。可以影响色相、饱和度,比如淡化成黑白,或者平滑地加强颜色饱和度。
可以通过将帧缓冲区混合到帧缓冲区上来实现 "smearing into the distance" 效果,方法是从 gl_FragCoord 稍微放大的 texcoord 中读取,例如 texture(textureSampler, (gl_FragCoord * 1.01).xy)
.
关于这一点,您不需要那些纹理坐标属性,您可以使用 gl_FragCoord 找出您在屏幕上的位置,并将其(调整后的副本)用于您的 texture
打电话。
查看 GLSLSandbox 上的一些着色器以获得灵感。
我在 GLSLSandbox 上完成了 a simple emulation of the trail effect。在真实的循环中,循环不存在,它会从一个小的偏移量中获取一个样本。 "loop" 效果会自行发生,因为它的输入包括最后一帧的输出。为了模拟最后一帧的纹理,我简单地做了它,这样我就可以计算出另一个像素是什么。在做轨迹效果时,你会读取最后一帧纹理而不是调用像 pixelAt 这样的东西。
你可以用波形代替我伪造的正弦波。使用 uv.x
到 select 的索引,适当缩放。
GLSL
#ifdef GL_ES
precision mediump float;
#endif
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
const float PI = 3.14159265358979323;// lol ya right, but hey, I memorized it
vec4 pixelAt(vec2 uv)
{
vec4 result;
float thickness = 0.05;
float movementSpeed = 0.4;
float wavesInFrame = 5.0;
float waveHeight = 0.3;
float point = (sin(time * movementSpeed +
uv.x * wavesInFrame * 2.0 * PI) *
waveHeight);
const float sharpness = 1.40;
float dist = 1.0 - abs(clamp((point - uv.y) / thickness, -1.0, 1.0));
float val;
float brightness = 0.8;
// All of the threads go the same way so this if is easy
if (sharpness != 1.0)
dist = pow(dist, sharpness);
dist *= brightness;
result = vec4(vec3(0.3, 0.6, 0.3) * dist, 1.0);
return result;
}
void main( void ) {
vec2 fc = gl_FragCoord.xy;
vec2 uv = fc / resolution - 0.5;
vec4 pixel;
pixel = pixelAt(uv);
// I can't really do postprocessing in this shader, so instead of
// doing the texturelookup, I restructured it to be able to compute
// what the other pixel might be. The real code would lookup a texel
// and there would be one sample at a small offset, the feedback
// replaces the loop.
const float e = 64.0, s = 1.0 / e;
for (float i = 0.0; i < e; ++i) {
pixel += pixelAt(uv + (uv * (i*s))) * (0.3-i*s*0.325);
}
pixel /= 1.0;
gl_FragColor = pixel;
}
我在 Java 中使用 OpenGL 构建了一个不错的音乐可视化工具。它看起来已经很整洁了,但我考虑过向它添加一些 post 处理。当时,它看起来像这样:
已经有一个用于记录输出的帧缓冲区,所以我有可用的纹理。现在我想知道是否有人对某些效果有想法。当前的片段着色器如下所示:
#version 440
in vec3 position_FS_in;
in vec2 texCoords_FS_in;
out vec4 out_Color;
//the texture of the last Frame by now exactly the same as the output
uniform sampler2D textureSampler;
//available data:
//the average height of the lines seen in the screenshot, ranging from 0 to 1
uniform float mean;
//the array of heights of the lines seen in the screenshot
uniform float music[512];
void main()
{
vec4 texColor = texture(textureSampler, texCoords_FS_in);
//insert post processing here
out_Color = texColor;
}
大多数 post 处理效果随时间变化,因此随时间变化的制服很常见。例如,可以通过使用 sin(elapsedSec * wavyRadsPerSec + (PI * gl_FragCoord.y * 0.5 + 0.5) * wavyCyclesInFrame)
.
一些 "postprocessing" 效果可以非常简单地完成,例如,您可以在整个屏幕上混合一个近乎黑色的透明四边形,而不是使用 glClear
清除后台缓冲区。这将创建一个持久性效果,过去的帧在当前帧后面逐渐变黑。
可以通过在距每个点的不同距离处获取多个样本,并对较近的样本进行更大的加权并求和来实现定向模糊。如果你跟踪一个点相对于相机位置和方向的运动,它可以做成运动模糊实现。
颜色变换也非常简单,只需将RGB 视为向量的XYZ,然后对其进行有趣的变换即可。棕褐色和 "psychedelic" 颜色可以通过这种方式产生。
您可能会发现将颜色转换为类似 HSV 的颜色、对该表示进行转换并将其转换回 RGB 以用于帧缓冲区写入很有帮助。可以影响色相、饱和度,比如淡化成黑白,或者平滑地加强颜色饱和度。
可以通过将帧缓冲区混合到帧缓冲区上来实现 "smearing into the distance" 效果,方法是从 gl_FragCoord 稍微放大的 texcoord 中读取,例如 texture(textureSampler, (gl_FragCoord * 1.01).xy)
.
关于这一点,您不需要那些纹理坐标属性,您可以使用 gl_FragCoord 找出您在屏幕上的位置,并将其(调整后的副本)用于您的 texture
打电话。
查看 GLSLSandbox 上的一些着色器以获得灵感。
我在 GLSLSandbox 上完成了 a simple emulation of the trail effect。在真实的循环中,循环不存在,它会从一个小的偏移量中获取一个样本。 "loop" 效果会自行发生,因为它的输入包括最后一帧的输出。为了模拟最后一帧的纹理,我简单地做了它,这样我就可以计算出另一个像素是什么。在做轨迹效果时,你会读取最后一帧纹理而不是调用像 pixelAt 这样的东西。
你可以用波形代替我伪造的正弦波。使用 uv.x
到 select 的索引,适当缩放。
GLSL
#ifdef GL_ES
precision mediump float;
#endif
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
const float PI = 3.14159265358979323;// lol ya right, but hey, I memorized it
vec4 pixelAt(vec2 uv)
{
vec4 result;
float thickness = 0.05;
float movementSpeed = 0.4;
float wavesInFrame = 5.0;
float waveHeight = 0.3;
float point = (sin(time * movementSpeed +
uv.x * wavesInFrame * 2.0 * PI) *
waveHeight);
const float sharpness = 1.40;
float dist = 1.0 - abs(clamp((point - uv.y) / thickness, -1.0, 1.0));
float val;
float brightness = 0.8;
// All of the threads go the same way so this if is easy
if (sharpness != 1.0)
dist = pow(dist, sharpness);
dist *= brightness;
result = vec4(vec3(0.3, 0.6, 0.3) * dist, 1.0);
return result;
}
void main( void ) {
vec2 fc = gl_FragCoord.xy;
vec2 uv = fc / resolution - 0.5;
vec4 pixel;
pixel = pixelAt(uv);
// I can't really do postprocessing in this shader, so instead of
// doing the texturelookup, I restructured it to be able to compute
// what the other pixel might be. The real code would lookup a texel
// and there would be one sample at a small offset, the feedback
// replaces the loop.
const float e = 64.0, s = 1.0 / e;
for (float i = 0.0; i < e; ++i) {
pixel += pixelAt(uv + (uv * (i*s))) * (0.3-i*s*0.325);
}
pixel /= 1.0;
gl_FragColor = pixel;
}