• 【Unity】屏幕空间位置变换到世界空间位置的方法


    屏幕空间像素的位置,是一个二维的浮点数,而世界空间的位置,则是三维的浮点数。实现的基本思路很简单,是世界空间位置变换到屏幕空间位置的逆过程,只是稍微有些区别。如果对图形渲染管线中的坐标变换没有弄清楚,或者习惯了Unity中直接调用封装好的函数,确实有些麻烦。

    简单的说,世界空间位置变换到屏幕空间位置的步骤是这样的:

    第一步,世界空间位置变换到裁剪空间

    float4 projectionPos=mul(UNITY_MATRIX_VP, float4(pos, 1.0));

    这里也可以分为两小步,世界空间变换到观察空间,观察空间变换到投影空间,也就是裁剪空间

    float4 viewPos=mul(UNITY_MATRIX_V, float4(worldPos, 1.0));

    float4 projectionPos=mul(UNITY_MATRIX_P, viewPos);

    第二步,裁剪空间变换到屏幕空间位置

    float4 projectionPos——>float2 screenPos

    这里也可以分为两小步,透视除法和屏幕映射

    透视除法,实际是将视椎体压平成为一个立方体。

    projectionPos.xyz/projectionPos.w;

    屏幕映射

    [-1,1]——>[0,1]——>屏幕的像素位置

    如果我们从屏幕空间位置反推世界空间位置的话,需要知道裁剪空间或观察空间的位置,然后直接乘逆矩阵变换到世界空间即可。

    但是如果要得到裁剪空间的位置,我们缺少w的信息,只有屏幕空间的位置xy,线性深度z(unity从深度贴图的直接取出的深度为非线性的)。

    因此,我们需要通过别的方式。根据摄像机的参数设置来计算得到观察空间的像素位置。

    1.计算观察空间的Z分量

    深度图采样,然后线性0-1,这时候得到的值是屏幕空间的线性深度Z。

    观察空间的Z计算即,观察空间的近裁减屏幕的位置+近裁剪平面与远裁剪平面之间的距离*线性深度。

    float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);
    float linearDepth=Linear01Depth(depth);
    float viewPosZ=_ProjectionParams.y+(_ProjectionParams.z-_ProjectionParams.y)*linearDepth;

    _ProjectionParams在Unity中的format为:

    float4 _ProjectionParams;
    // x = 1 or -1 (-1 if projection is flipped)
    // y = near plane
    // z = far plane
    // w = 1/far plane

    2.计算观察空间Z为camPosZ处视椎体切面的高度和宽度

    float height = 2 * camPosZ / unity_CameraProjection._m11;                 
    float width = _ScreenParams.x / _ScreenParams.y * height;  

    unity_CameraProjection是摄像机的投影矩阵,具体的内容可以看Unity的API文档,unity_CameraProjection._m11的内容是:

    unity_CameraProjection._m11= 2.0F * near / (top - bottom);

    这里的near就是计算得到的camPosZ,top-bottom即height。

    知道高度和屏幕宽高的比例后,就可以计算宽度

    // x = width
    // y = height
    // z = 1 + 1.0/width
    // w = 1 + 1.0/height
    float4 _ScreenParams;

    3.根据高度和宽度,以及屏幕空间位置UV,得到观察空间的XY坐标

    float camPosX = width * uv.x - width / 2;  
    float camPosY = height * uv.y - height / 2; 

    这里是一个平移的操作,在屏幕空间,原点位于左下角。但是在观察空间的视椎体切面上,原点位于屏幕的中心。所以原来位于屏幕坐标系的坐标(x,y),在平面中的坐标为(x-0.5,y-0.5)。

    4.在熟悉了原理之后,以上步骤可以简单用以下的矩阵运算实现:

    float zdepth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv.xy);
    float4 clipPos = float4(i.uv.xy, depth, 1.0);
    clipPos.xyz = 2.0f * clipPos.xyz - 1.0f;
    float4 camPos = mul(unity_CameraInvProjection, clipPos);
    camPos.xyz /= camPos.w;
    camPos.z *= -1;

    5.最后,矩阵乘法变换观察空间到世界空间

     float3 worldPos=mul(unity_CameraToWorld, camPos).xyz;  
  • 相关阅读:
    POJ 3253 Fence Repair STL 优先队列
    P1196 [NOI2002]银河英雄传说 题解
    UVA1316 Supermarket 题解
    P1955 [NOI2015]程序自动分析 题解
    P3807 【模板】卢卡斯定理 题解
    P2480 [SDOI2010]古代猪文 题解
    题解 P4778 【Counting swaps】
    P1313 计算系数 题解
    P3810 【模板】三维偏序(陌上花开)题解
    P1072 Hankson 的趣味题 题解
  • 原文地址:https://www.cnblogs.com/jaffhan/p/7448579.html
Copyright © 2020-2023  润新知