Calculate clipspace.w from clipspace.xyz and (inv) projection matrix

后端 未结 1 2053
梦如初夏
梦如初夏 2021-02-04 19:07

I\'m using a logarithmic depth algorithmic which results in someFunc(clipspace.z) being written to the depth buffer and no implicit perspective divide.

I\'m doi

1条回答
  •  再見小時候
    2021-02-04 19:43

    clipspace.xy = FragCoord.xy / viewport * 2.0 - 1.0;
    

    This is wrong in terms of nomenclature. "Clip space" is the space that the vertex shader (or whatever the last Vertex Processing stage is) outputs. Between clip space and window space is normalized device coordinate (NDC) space. NDC space is clip space divided by the clip space W coordinate:

    vec3 ndcspace = clipspace.xyz / clipspace.w;
    

    So the first step is to take our window space coordinates and get NDC space coordinates. Which is easy:

    vec3 ndcspace = vec3(FragCoord.xy / viewport * 2.0 - 1.0, depth);
    

    Now, I'm going to assume that your depth value is the proper NDC-space depth. I'm assuming that you fetch the value from a depth texture, then used the depth range near/far values it was rendered with to map it into a [-1, 1] range. If you didn't, you should.

    So, now that we have ndcspace, how do we compute clipspace? Well, that's obvious:

    vec4 clipspace = vec4(ndcspace * clipspace.w, clipspace.w);
    

    Obvious and... not helpful, since we don't have clipspace.w. So how do we get it?

    To get this, we need to look at how clipspace was computed the first time:

    vec4 clipspace = Proj * cameraspace;
    

    This means that clipspace.w is computed by taking cameraspace and dot-producting it by the fourth row of Proj.

    Well, that's not very helpful. It gets more helpful if we actually look at the fourth row of Proj. Granted, you could be using any projection matrix, and if you're not using the typical projection matrix, this computation becomes more difficult (potentially impossible).

    The fourth row of Proj, using the typical projection matrix, is really just this:

    [0, 0, -1, 0]
    

    This means that the clipspace.w is really just -cameraspace.z. How does that help us?

    It helps by remembering this:

    ndcspace.z = clipspace.z / clipspace.w;
    ndcspace.z = clipspace.z / -cameraspace.z;
    

    Well, that's nice, but it just trades one unknown for another; we still have an equation with two unknowns (clipspace.z and cameraspace.z). However, we do know something else: clipspace.z comes from dot-producting cameraspace with the third row of our projection matrix. The traditional projection matrix's third row looks like this:

    [0, 0, T1, T2]
    

    Where T1 and T2 are non-zero numbers. We'll ignore what these numbers are for the time being. Therefore, clipspace.z is really just T1 * cameraspace.z + T2 * cameraspace.w. And if we know cameraspace.w is 1.0 (as it usually is), then we can remove it:

    ndcspace.z = (T1 * cameraspace.z + T2) / -cameraspace.z;
    

    So, we still have a problem. Actually, we don't. Why? Because there is only one unknown in this euqation. Remember: we already know ndcspace.z. We can therefore use ndcspace.z to compute cameraspace.z:

    ndcspace.z = -T1 + (-T2 / cameraspace.z);
    ndcspace.z + T1 = -T2 / cameraspace.z;
    cameraspace.z = -T2 / (ndcspace.z + T1);
    

    T1 and T2 come right out of our projection matrix (the one the scene was originally rendered with). And we already have ndcspace.z. So we can compute cameraspace.z. And we know that:

    clispace.w = -cameraspace.z;
    

    Therefore, we can do this:

    vec4 clipspace = vec4(ndcspace * clipspace.w, clipspace.w);
    

    Obviously you'll need a float for clipspace.w rather than the literal code, but you get my point. Once you have clipspace, to get camera space, you multiply by the inverse projection matrix:

    vec4 cameraspace = InvProj * clipspace;
    

    0 讨论(0)
提交回复
热议问题