steps3D - Tutorials - Реализация эффекта motion blur

Реализация эффекта motion blur

Одним из эффектов, естественным образом получающихся при использовании камеры (но отсутствующих при рендеринге) является motion blur, т.е. размытие при движении.

Этот эффект естественным образом получается при съемке на камеру поскольку съемка одного кадра это не мгновенный процесс - он занимает некоторое время. Объекты, быстро двигающиеся на протяжении этого времени получаются размытыми.

Когда мы имеем дело с рендерингом в реальном времени, то имеет смысл разделить размытие, возникающее при движении/ повороте камеры, и размытие, возникающее при быстром движении объектов относительно камеры. Здесь мы рассмотрим только первый случай - размытие, возникающее при движении и повороте камеры - как можно реализовать данный эффект за счет постобработки результата рендеринга.

Будем считать что для текущего кадра у нас уже есть результаты рендеринга в виде изображения с освещением и G-буфера (на самом деле нам из него понадобится только значение zeye). В фрагментном шейдере постобработки мы по значению zeye восстановим трехмерные координаты Peye соответствующей точки в системе координат камеры.

Далее мы используем обратную модельную матрицу для того, чтобы перевести эту точку в мировую систему координат Pw. После этого мы берем модельную матрицу с предыдущего кадра Mprev и матрицу проектирования для того, чтобы определить положение этой точки на экране для предыдущего кадра.

Тем самым мы для точки имеем текущее положение на экране и предыдущее положение на экране. После этого мы просто берем несколько точек на отрезке соединяющем эти точки и находим среднее значение цвета в них. Это и будет требуемое размытие.

Ниже приводится соответствующий шейдер.

-- vertex

#version 330 core

layout(location = 0) in vec4 pos;

out vec2 tex;

void main(void)
{
    tex = 0.5 * ( pos.xy + vec2 ( 1.0 ) );
    gl_Position = vec4 ( pos.xy, 0.0, 1.0 );
}

-- fragment

#version 330 core

uniform mat4 proj;                // we will need it in pos reconstruction
uniform mat4 mv;
uniform mat4 mvPrevInv;           // inverted model-view matrix from previous frame

uniform sampler2D colorMap;
uniform sampler2D nzMap;

in  vec2  tex;
out vec4  color;

const int samples  = 7;   

    // Note: it assumes proj is classical projection matrix
    // (not multiplied by smth else)
vec3    getViewPos ( in vec2 uv )
{
    vec4    nz = texture ( nzMap, uv );        // eyeZ in w
    vec2    a  = vec2    ( -2.0 / proj [0][0], -2.0 / proj [1][1] );
    vec2    b  = vec2    (  1.0 / proj [0][0],  1.0 / proj [1][1] );
    
    return nz.w * vec3 ( a*uv + b, 1.0 );
}

void    main ()
{
    vec3    pos = getViewPos ( tex );
    vec4    p0  = mvPrevInv * vec4 ( pos, 1.0 );
    vec4    p   = proj * mv * p0;
   
    p.xyz /= p.w;       // perspective division
    p.xy   = 0.5 * ( p.xy + vec2 ( 1.0 ) );

    vec2    v = tex - p.xy;
    vec2    uv  = tex;
    vec4    sum = texture ( colorMap, uv );

    for ( int i = 1; i < samples; i++ )
    {
        uv  += v / 3.0;
        sum += texture ( colorMap, uv );
    }

    color = sum / samples;
}

Рис. Изображение в эффектом motion blur

Исходный код для этого примера на python можно скачать из репозитория.