OpenGL屏幕后期处理效果

时间:2016-04-06 06:56:23

标签: opengl shader lwjgl

我在Java中使用OpenGL构建了一个很好的音乐可视化工具。它已经看起来非常整洁,但我已经考虑过为它添加一些后期处理。当时,它看起来像这样:

Something like this

已经有一个用于记录输出的帧缓冲,因此我已经可以使用纹理。现在我想知道是否有人对某些效果有所了解。当前的Fragment着色器如下所示:

#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;
}

1 个答案:

答案 0 :(得分:3)

大多数后期处理效果随时间而变化,因此通常有一个随时间变化的制服。例如,"波浪"可以使用sin(elapsedSec * wavyRadsPerSec + (PI * gl_FragCoord.y * 0.5 + 0.5) * wavyCyclesInFrame)偏移纹理坐标来创建效果。

一些"后处理"效果可以非常简单地完成,例如,不是用glClear清除后缓冲区,而是可以在整个屏幕上混合近乎黑色的透明四边形。这将创建一个持久效果,其中过去的帧在当前帧之后渐渐变为黑色。

可以通过从每个点以不同距离拍摄多个样本来实现定向模糊,并且更强地对更近的样本进行加权并求和。如果您跟踪相对于摄像机位置和方向的点的运动,则可以将其设置为运动模糊实现。

颜色转换也非常简单,只需将RGB视为矢量的XYZ,并对其进行有趣的转换。棕褐色和迷幻"迷幻"颜色可以这样生产。

您可能会发现将颜色转换为类似HSV的内容,对该表示进行转换以及将其转换回RGB以进行帧缓冲写入会很有帮助。您可能会影响色调,饱和度,例如,淡化为黑白,或平滑地增强色彩饱和度。

A"涂抹在远处"效果可以通过将帧缓冲区混合到帧缓冲区来完成,方法是读取从gl_FragCoord稍微扩展的texcoord,如texture(textureSampler, (gl_FragCoord * 1.01).xy)

在该注释中,您不需要这些纹理坐标属性,您可以使用gl_FragCoord找出您在屏幕上的位置,并使用(调整后的副本)来进行texture调用。

GLSLSandbox上查看一些着色器以获取灵感。

我在GLSLSandbox上完成了a simple emulation of the trail effect。在实际的循环中,循环将不存在,它将从一个小偏移量中获取一个样本。 "循环"效果会自行发生,因为它的输入包括最后一帧的输出。为了模拟最后一帧的纹理,我简单地做了它,这样我就可以计算出其他像素是什么。你可以读取最后一帧纹理,而不是在做尾迹效果时调用pixelAt之类的东西。

你可以用波而不是我假的正弦波。使用uv.x选择适当缩放的索引。

<强> 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;
}