Nvidia DirectX10 SSAO简要翻译

详细资料:

http://developer.download.nvidia.com/SDK/10.5/direct3d/Source/ScreenSpaceAO/doc/ScreenSpaceAO.pdf ; http://developer.download.nvidia.com/presentations/2008/SIGGRAPH/HBAO_SIG08b.pdf

 

主要计算方式:

在屏幕空间进行射线检测,比较其与XY平面的夹角Sin值,最后进行积分,获得整个半球面的AO值。

图示如下:

ao计算公式

需要补充几点:

法线是基于像素的,不能插值 ,但可通过ddx/ddy微分计算

角度加个偏移值,防止采样点的sin h过小

ao衰减参数 W(r) = 1 -((s-p)/R)2

半球面ao积分计算

 

 

流程图,最后分别对X方向和Y方向进行高斯平滑即可。

具体shader代码如下(ssao.frag):

// 参数说明: m_R,相机坐标系ao计算半径;m_NumSteps,每方向的计算次数;m_NumDir,随机方向个数;m_inv_R,半径倒数;

// m_sqr_R,半径平方;m_FocalLen,Vector2f(1f / (tanFovY * aspectRatio), 1f / tanFovY);m_Resolution,ao

// depthTexture的宽高;m_Contrast, 3;

float M_PI = 3.14159265;

varying vec2 texCoord;
uniform sampler2D m_DepthTexture;

vec3 uv_to_eye(vec2 uv, float z) {
    // texture origin is at bottom left corner in OpenGL
    uv = (uv * vec2(2.0, 2.0) - vec2(1.0, 1.0));
   
    return vec3(uv * m_InvFocalLen * z, z);
}

vec3 fetch_eye_pos(vec2 uv) {
    float z = texture2D(m_DepthTexture, uv).x;

    z = m_invFrustumFar / (1.0 - z);// (-2.0 * 10000 / (z * 9999 - 10001)) / 9999; to be precise, but only approximation is required
    return uv_to_eye(uv, z);
}

float length2(vec3 v) {
    return dot(v, v);
}

vec3 min_diff(vec3 P, vec3 Pr, vec3 Pl)
{
    vec3 V1 = Pr - P;
    vec3 V2 = P - Pl;
    return (length2(V1) < length2(V2)) ? V1 : V2;
}

vec2 rotate_direction(vec2 Dir, vec2 CosSin)
{
    return vec2(Dir.x * CosSin.x - Dir.y * CosSin.y, Dir.x * CosSin.y + Dir.y * CosSin.x);
}

vec2 snap_uv_offset(vec2 uv)
{
    return round(uv * m_Resolution) * m_InvResolution;
}

vec3 tangent_vector(vec2 deltaUV, vec3 dPdu, vec3 dPdv)
{
    return deltaUV.x * dPdu + deltaUV.y * dPdv;
}

float tangent(vec3 T)
{
    return -T.z / length(T.xy);
}

vec2 snap_uv_coord(vec2 uv)
{
    return uv - (fract(uv * m_Resolution) - 0.5) * m_InvResolution;
}

float tan_to_sin(float x)
{
    return x / sqrt(1.0 + x * x);
}

float falloff(float r)
{
    return 1.0 - m_Attenuation * r * r;
}

float tangent(vec3 P, vec3 S)
{
    return (P.z - S.z) / length(S.xy - P.xy);
}

float AccumulatedHorizonOcclusion_Quality(vec2 deltaUV,
                                          vec2 uv0,
                                          vec3 P,
                                          float numSteps,
                                          float randstep,
                                          vec3 dPdu,
                                          vec3 dPdv)
{
    // Jitter starting point within the first sample distance
    vec2 uv = (uv0 + deltaUV) + randstep * deltaUV;
   
    // Snap first sample uv and initialize horizon tangent
    vec2 snapped_duv = snap_uv_offset(uv - uv0);
    vec3 T = tangent_vector(snapped_duv, dPdu, dPdv);
    float tanH = tangent(T) + m_TanAngleBias;

    float ao = 0.0;
    float h0 = 0.0;
    for(float j = 0.0; j < numSteps; ++j) {
        vec2 snapped_uv = snap_uv_coord(uv);
        vec3 S = fetch_eye_pos(snapped_uv);
        uv += deltaUV;

        // Ignore any samples outside the radius of influence
        float d2 = length2(S - P);
        if (d2 < m_sqr_R) {
            float tanS = tangent(P, S);

            if (tanS > tanH) {
                // Compute tangent vector associated with snapped_uv
                vec2 snapped_duv = snapped_uv - uv0;
                vec3 T = tangent_vector(snapped_duv, dPdu, dPdv);
                float tanT = tangent(T) + m_TanAngleBias;

                // Compute AO between tangent T and sample S
                float sinS = tan_to_sin(tanS);
                float sinT = tan_to_sin(tanT);
                float r = sqrt(d2) * m_inv_R;
                float h = sinS - sinT;
                ao += falloff(r) * (h - h0);
                h0 = h;

                // Update the current horizon angle
                tanH = tanS;
            }
        }
    }
    return ao;
}

void main() {

    vec3 P = fetch_eye_pos(texCoord);

    // Project the radius of influence m_R from eye space to texture space.
    // The scaling by 0.5 is to go from [-1,1] to [0,1].
    vec2 step_size = 0.5 * m_R  * m_FocalLen / P.z;

    // Early out if the projected radius is smaller than 1 pixel.
    float numSteps = min (m_NumSteps, min(step_size.x * m_Resolution.x, step_size.y * m_Resolution.y));
    if( numSteps < 1.0 ) {
        gl_FragColor = vec4(1.0);
        return;
    }

    step_size = step_size / ( numSteps + 1.0 );

    // Nearest neighbor pixels on the tangent plane
    vec3 Pr, Pl, Pt, Pb;

    Pr = fetch_eye_pos(texCoord + vec2(m_InvResolution.x, 0));
    Pl = fetch_eye_pos(texCoord + vec2(-m_InvResolution.x, 0));
    Pt = fetch_eye_pos(texCoord + vec2(0, m_InvResolution.y));
    Pb = fetch_eye_pos(texCoord + vec2(0, -m_InvResolution.y));

    // Screen-aligned basis for the tangent plane
    vec3 dPdu = min_diff(P, Pr, Pl);
    vec3 dPdv = min_diff(P, Pt, Pb) * (m_Resolution.y * m_InvResolution.x);

    vec2 jitter = texCoord * 1000.0;
    vec3 rand = normalize(vec3(sin(jitter.x) + sin(jitter.y), cos(jitter.x) + cos(jitter.y), 0));

    float ao = 0.0;
    float d;
    float alpha = 2.0 * M_PI / m_NumDir;


    for (d = 0.0; d < m_NumDir; d++) {
        float angle = alpha * float(d);
        vec2 dir = vec2(cos(angle), sin(angle));
        vec2 deltaUV = rotate_direction(dir, rand.xy) * step_size.xy;
        ao += AccumulatedHorizonOcclusion_Quality(deltaUV, texCoord, P, numSteps, rand.z, dPdu, dPdv);
    }

    gl_FragColor = vec4(vec3(1.0 - ao / m_NumDir * m_Contrast), 1);
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值