Tuesday, December 10, 2013

Sponza рэндэрлэх оролдлого 15, Depth Of Field, Tilt Shift

Depth of Field бол миний хамгийн дуртай эффектүүдийн нэг. Энийг хэрэгжүүлж үзэх юмсан гэж нэлээн удаан бодож явлаа. Хүсэл байвч хясна гэгчээр тухайн үед ямар ч shader програмчлалын талаар ойлголтгүй матриц гэж юу болохоо ч сайн мэдээгүй байсан юм.


DOF-ийг програмчлахын тулд frame buffer доторхи depth текстурээс тухайн фрагмент нь камераас хэр хол байгаа талаархи мэдээллийг нь ялган авах хэрэгтэй. depth texture гэдэг бол MVP матрицаар дамжсаар Homogeneous огторгуй доторхи Z-утга юм мэдээж камерийн байрлал бол тооллын эх байна. DOF эффектийг ажиллуулахын тулд энэ Z утга нь view-space дотор яг хэр хол хэмжээтэй байгааг мэдэх шаардлагатай. Хэрэв тэгж чадвал камерийн фокусын интервалаас гадуурх Z утгатай фрагментүүдийг бүрсийлгэх буюу DOF эффект оруулах боломж бүрдэх юм.

хамгийн эхний алхам бол depth texture доторхи homogeneous-space д байгаа Z утгыг олж авах хэрэгтэй. Энд texCoord бол screen aligned quad дээрхи пикселийн байрлал буюу програмын цонхон дээрхи пикселийн байрлалыг агуулна. Харин depthTexture нь FBO-оос гаргаж авсан depth мэдээллийг агуулах текстур юм.
float depth = textured2D(depthTexture, texCoord);

Depth texture нь гэж нэг иймэрхүү юм байдаг. depth+stencil хоёрыг pack-лаад хадгалцан текстур, пиксел бүр нь гүний мэдээлэл болон mask мэдээллүүдийг агуулна гэсэн үг.




эндээс homogeneous-space доторхи цэгийн координатыг гаргаж авах хэрэгтэй.
vec4 screenPosition = vec4(
        vCoord.x,
        vCoord.y,
        depth,
        1.0
    ) * 2.0 - 1.0;

одоо homogeneous-space доторхи цэгийг view-space рүү хөрвүүлж view-space доторхи цэгийг гаргаж авах хэрэгтэй. view-space ээс homogeneous-space руу хөрвүүлэхэд projection матриц ашигладаг. Харин энэ тохиолдолд бол үүний урвуу матриц хэрэг болно, inversed projection matrix гэсэн үг.
inverse projection matrix ийг үндсэн програм дээрээ glm ашиглаж гаргаж аваад shader-лүү Uniform байдлаар дамжуулах хэрэгтэй.
glm::mat4 projectionInverseMatrix = glm::inverse(projectionMatrix);
....
glUniformMatrix4fv(glGetUniformLocation(dofProgramID, "projectionInverseMatrix"), 1, GL_FALSE, glm::value_ptr(projectionInverseMatrix));

цэгээ view-space рүү оруулж ирэхдээ
vec4 viewPosition = projectionInverseMatrix * screenPosition;

камераас хэр хол байгаа мэдээллийг гаргаж авахдаа homogenous координатыг нь нормчилж байж авах хэрэгтэй.
float z = -(viewPosition.z / viewPosition.w);
Харин одоо энэ Z утгаа ашиглан дураараа дургиж болно. Жишээ нь камерийн фокусын интервалд хамрагдах хэсгийг нь рэндэрлээд бусад хэсэгт нь өөр өнгө онооё
if (z < 50 || z>350) {
    out_color = vec4(1.0, 1.0, 0.5, 1.0);
} else {
    out_color = texture2D(uScreenTex, vCoord);
}

Үүний үр дүнд DOF-д бүрсэлзүүлэх шаардлагатай хэсэгүүд нь харагдаж байна.


Одоо энэ зураг дээрхи шар хэсгүүдээ өмнөх постод тэмдэглэсэн gaussian blur алгоритмыг ашиглан бүрсэлзүүлье
#version 330
in vec2 vCoord;

uniform sampler2D uScreenTex;
uniform sampler2D uDepthTex;
uniform vec2      pixelSize;
uniform int       isVertical;
uniform float     aspectRatio;
uniform float     focus;
uniform mat4      projectionInverseMatrix;

out vec3 out_color;

const float blurClamp = 3.0;
const float bias      = 0.6;

const float weights[5] = float[](0.12, 0.22, 0.32, 0.22, 0.12);

void main(void)
{
    float depth = texture2D(uDepthTex, vCoord);
    vec4 screenPosition = vec4(
        vCoord.x,
        vCoord.y,
        depth,
        1.0
    ) * 2.0 - 1.0;
    vec4 viewPosition = projectionInverseMatrix * screenPosition;
    float z = -(viewPosition.z / viewPosition.w);

    if (z < 50 || z>350) {
        vec2 values[5];
        if (isVertical==1) {
            values = vec2[](
                vec2(0.0, -pixelSize.y * 3),
                vec2(0.0, -pixelSize.y * 2),
                vec2(0.0,  pixelSize.y),
                vec2(0.0,  pixelSize.y * 2),
                vec2(0.0,  pixelSize.y * 3)
            );
        } else {
            values = vec2[](
                vec2(-pixelSize.y * 3, 0.0),
                vec2(-pixelSize.y * 2, 0.0),
                vec2( pixelSize.y    , 0.0),
                vec2( pixelSize.y * 2, 0.0),
                vec2( pixelSize.y * 3, 0.0)
            );
        }
        for (int i=0; i<5; i++) {
            vec3 temp = texture2D(uScreenTex, vCoord + values[i]).xyz;  
            out_color += temp * weights[i];
        }

    } else {
        out_color = texture2D(uScreenTex, vCoord);
    }    
}
Ингээд үр дүнд нь Depth Of Field эффектийг гаргаж авлаа.