• 基于Unity ComputeShader 实现正向DIBR


    什么是DIBR?

    就是根据1张或者n张带有深度信息的图片,也就是RGBD图,通过插值的方式,生成出虚拟视点下的效果图。

    深度信息如果不够精确,效果上,容易出现一些错误。

    以及视角的变化,导致一些信息不足,会存在空洞问题。

    如果在Unity中怎么实现呢。

    通过DIBR算法,在场景中预制两个相机,通过脚本分别获取颜色图和深度图。

    然后通过computeshader,将两个深度图插值为中间视点的图。

    根据几何关系,可以推导出左右视图的关系。

    相关像素的水平视差 dispararity满足如上公式。

    这是针对一般用相机拍摄的图片来说的。f代表相机的焦距。

    在unity下的相机,没有焦距的概念。怎么办呢。

    由于以上公式,都是以物理实际距离尺度计算的。需要变换到像素空间尺度下,便于计算和插值,得到像素关系。

    所以上面的公式,两面同时除以像素宽度pitch。

    像素尺度视差=disparity/pitch=B*f/(Z*pitch)=B/Z*f/pitch

    令F=f/pitch得到B/Z*F

    这个F刚好可以推导出和fov的关系。

     可以假定有个投影面,仿照真实相机的物理结构。

    投影面也就是cmos感光元器件。假设水平分辨率为Res,每个像素宽度为pitch。

    所以有w=Res*pitch。

    又有,w/2/f=tan(fov/2)

    所以Res*pitch/2/f=tan(fov/2)

    可以推导出 F=f/pitch=f*Res/w=Res/(2tan(fov/2))

    比如水平fov=90度,分辨率是3840,那么F=1920

    可以看出F只和fov和分辨率有关了。

    于是根据左图的深度图得到深度(需要从0-1区间变换到view坐标系)

    和两个相机的距离,就可以插值出虚拟视点的图了。大概就是这个原理。

    其中最大视差的计算方法为 B/near*F  因为near平面的视差最大。

    如果有最大视差要求,怎么判端两个相机最远能离开多远呢?

    B=near*最大视察/F。

    例如最大视差为64,near=0.3 F=1920 那么相机最大距离为 0.01

    也就是两个相机距离1厘米时,最大视差是64个像素。

    这个用在反向DIBR时也很有用(因为需要遍历搜索,所以估算遍历次数很重要)

    以下是C#源代码。

    首先是获取相机深度的代码;挂载相机物体上。

    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. public class RenderWithDepth : MonoBehaviour
    5. {
    6. public RenderTexture colorRT;
    7. public RenderTexture depthRT;
    8. // Start is called before the first frame update
    9. void Start()
    10. {
    11. colorRT = new RenderTexture(3840, 2160, 0);
    12. depthRT = new RenderTexture(3840, 2160, 24, RenderTextureFormat.Depth);
    13. GetComponent().SetTargetBuffers(colorRT.colorBuffer, depthRT.depthBuffer);
    14. }
    15. }

    其次是C#和computeshader代码,挂载到虚拟视点相机上。

    1. public class RenderByCSDIBR : MonoBehaviour
    2. {
    3. int width = 3840;
    4. int height = 2160;
    5. public ComputeShader dibrCS;
    6. public RenderWithDepth lDep, rDep;
    7. RenderTexture resultRT_L, resultRT_R;
    8. // Start is called before the first frame update
    9. void Start()
    10. {
    11. resultRT_L = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32);
    12. resultRT_L.enableRandomWrite = true;
    13. resultRT_L.Create();
    14. resultRT_R = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32);
    15. resultRT_R.enableRandomWrite = true;
    16. resultRT_R.Create();
    17. }
    18. public void ClearOutRenderTexture(RenderTexture renderTexture)
    19. {
    20. RenderTexture rt = RenderTexture.active;
    21. RenderTexture.active = renderTexture;
    22. GL.Clear(true, true, Color.clear);
    23. RenderTexture.active = rt;
    24. }
    25. private void OnPostRender()
    26. {
    27. ClearOutRenderTexture(resultRT_L);
    28. ClearOutRenderTexture(resultRT_R);
    29. int k1 = dibrCS.FindKernel("CSMain");
    30. dibrCS.SetTexture(k1, "ResultL", resultRT_L);
    31. dibrCS.SetTexture(k1, "ResultR", resultRT_R);
    32. dibrCS.SetTexture(k1, "LC", lDep.colorRT);
    33. dibrCS.SetTexture(k1, "RC", rDep.colorRT);
    34. dibrCS.SetTexture(k1, "LD", lDep.depthRT);
    35. dibrCS.SetTexture(k1, "RD", rDep.depthRT);
    36. dibrCS.SetFloat("_LCamPos", lDep.transform.position.x);
    37. dibrCS.SetFloat("_RCamPos", rDep.transform.position.x);
    38. dibrCS.SetFloat("_CurCamPos", transform.position.x);
    39. dibrCS.SetFloat("_Far", 1000);
    40. dibrCS.SetFloat("_Near", 0.3f);
    41. dibrCS.Dispatch(k1, width / 8, height / 8, 1);
    42. }
    43. private void OnRenderImage(RenderTexture source, RenderTexture destination)
    44. {
    45. Graphics.Blit(resultRT_L, destination);
    46. }
    47. }

     computeshader代码

    1. #pragma kernel CSMain
    2. #define RES_W 3840
    3. // Create a RenderTexture with enableRandomWrite flag and set it
    4. // with cs.SetTexture
    5. RWTexture2D ResultL, ResultR;
    6. Texture2D LC, RC;
    7. Texture2D LD, RD;
    8. float _LCamPos, _RCamPos, _CurCamPos;
    9. float _Far, _Near;
    10. float LinearEyeDepth(float _z)
    11. {
    12. float x = 1.0 - _Far / _Near;
    13. float y = _Far / _Near;
    14. float z = x / _Far;
    15. float w = y / _Far;
    16. return 1.0 / (z * _z + w);
    17. }
    18. float getViewZ(uint2 pos, bool isLeft) {
    19. float z;
    20. if (isLeft)
    21. z = LinearEyeDepth(1-LD[pos].x);
    22. else
    23. z = LinearEyeDepth(1-RD[pos].x);
    24. return z;
    25. }
    26. [numthreads(8,8,1)]
    27. void CSMain (uint3 id : SV_DispatchThreadID)
    28. {
    29. // TODO: insert actual code here!
    30. float maxBaseLine = _RCamPos - _LCamPos;
    31. float fov = radians(90.0f);
    32. float near = 0.3f;
    33. float focal = RES_W / (2.0f * tan(fov / 2));
    34. float maxDis = maxBaseLine * focal / _Near;
    35. float baselineL = _CurCamPos - _LCamPos;
    36. float baselineR = maxBaseLine - baselineL;
    37. // 左眼视图
    38. float z = getViewZ(id.xy, true);
    39. int dis = int(baselineL * focal / z);
    40. int x = max(0, id.x - dis);
    41. float dis_h = int(dis / 256) / 255.0f;
    42. float dis_l = int(dis % 256) / 255.0f;
    43. float4 cur = ResultR[int2(x, id.y)];
    44. float discur = cur.x * 255 * 256 + cur.y * 255;
    45. float4 col = LC[id.xy];
    46. if (dis > discur) {
    47. ResultL[int2(x, id.y)] = col;//float4(dis_h, dis_l, 0, 1);
    48. ResultR[int2(x, id.y)] = float4(dis_h, dis_l, 0, 1);
    49. }
    50. //Result[id.xy] = float4(z, z, z, 1);
    51. // 右眼视图
    52. z = getViewZ(id.xy, false);
    53. dis = uint(baselineR * focal / z);
    54. x = min(RES_W-1, id.x + dis);
    55. dis_h = int(dis / 256) / 255.0f;
    56. dis_l = int(dis % 256) / 255.0f;
    57. cur = ResultR[uint2(x, id.y)];
    58. discur = cur.x * 255 * 256 + cur.y * 255;
    59. col = RC[id.xy];
    60. if (dis > discur) {
    61. ResultL[int2(x, id.y)] = col;//float4(dis_h, dis_l, 0, 1);
    62. ResultR[int2(x, id.y)] = float4(dis_h, dis_l, 0, 1);// RC[id.xy];
    63. }
    64. }

  • 相关阅读:
    贪吃蛇项目实践!(下)
    计算机网路复习01
    在nodejs中实现实时通信的几种方式
    面试题解答:Spring Lifecycle 和 SmartLifecycle 有何区别?
    用装配坚果流水线的场景讲解testng测试框架,一学就会,很是有趣
    【小沐学C++】C++ 基于Premake构建工程项目(Windows)
    C# 多态性
    这几个必备的vscode插件,你安装了几个
    从零开始写 Makefile
    糖尿病新世界杂志糖尿病新世界杂志社糖尿病新世界编辑部2022年第12期目录
  • 原文地址:https://blog.csdn.net/hakukou/article/details/126365232