继续练习一个模拟冰块的效果,模拟的是一个不透明内部有杂质的冰块,内部杂质用视差映射来实现,表面就是简单的法线贴图+Cubemap反射采样,也可以直接只计算高光不反射图案。文章会把视差映射讲一下,算是对学习的记录和总结。

Parallax Mapping视差映射

好多人的文章里都写到:

视差映射是法线映射的增强版,不止改变了光照作用,还在平坦的多边形上创建了3D细节的假象

其实我们看原理和代码能看出来,视差映射只是增加了高低错落的感觉,并没有扰动表面法线改变光照结果,所以在需要的时候我们还是要通过法线贴图+视差映射来渲染出想要的视觉效果。

视差映射只需要用到一个浮点值,也就是只需要贴图中的一个通道就可以表达,所以经常保存在法线贴图的A通道或者其他贴图的其中一个通道中。这个通道中的值保存的是对应的每个点要沉入物体表面多少深度而不是高出表面的高度,所以也可以叫做深度图。

视差映射只是对视向量进行了偏移,对主贴图进行偏移采样来达到让平面看起来是立体的效果。物体表面本身还是原来的样子,顶点并没有发生偏移,高低落差只是一个假象。

当前片元是图中的T0,视向量为V,纹理左边对深度图采样得到当前的深度是0.55,所以V碰到的并不是T0,而是继续向前延伸碰到的高度为0.15的点,对应的纹理坐标是T1,所以应该使用T1的纹理坐标进行主纹理和法线的采样。

简单视差映射 Parallax Mapping

带偏移上限的视差映射 Parallax Mapping With Offse Limiting

只取一步近似计算得到新纹理坐标是最简单的视差映射,被直接成为视差映射。

视差映射只有在高度相对平滑,并且不存在复杂细节时,才能得到相对可以接受的结果。如果视向量和表面法线夹角过大的话就会出现严重错误的结果。

偏移纹理坐标的方法:

切线空间是沿着物体表面建立的,法线垂直于表面,而TB分量和纹理坐标的xy分量重合,所以视向量V的z分量为法线分量,xy分量和纹理的xy分量重合,所以视向量的xy分量可以不加换算的直接用作纹理坐标来计算偏移。用xy除以z,就是视差映射技术中对纹理坐标偏移的原始计算。

如果不除以z,得到的就是带偏移上限的视差映射,带偏移上线的视差映射可以避免在向量V和法向量N夹角太大时的一些错误的结果。

然后把V的xy分量加到T0的纹理坐标上,并且和T0纹理的深度值H(T0)相乘,就得到了沿着V方向的新的纹理坐标。

然后用一个scale系数来控制视差映射效果的幅度。把scale乘给V的xy分量。最有意义的值在0-0.5之间。更高的值会得到错误的映射计算。也可以把scale设为负数,这样的话法向量的z分量需要反转过来进行计算。

从上图中可以看出,视向量正确的交点和偏移后的采样点Tp差距还是很大的,所以视差映射和带偏移上限的视差映射在视向量与法线的夹角越大的情况下误差就会越大。

陡峭视差映射 Steep Parallax Mapping

陡峭视差映射,不是简单的视差近似,不只是简单粗暴的对纹理坐标进行偏移而不进行合理性检查,会检查结果是否接近于正确值。陡峭视差映射是将深度分割为等距的若干层,然后从0层开始采样高度图,每一次会沿着V的方向偏移纹理坐标,如果采样的深度已经大于当前层的深度,停止检查并使用最后一次采样的纹理坐标作为结果。

以上图为例,深度被分割为8层,每层高度为0.125,每层的纹理坐标偏移是V.xy/V.z*sclae/numLayer,从黄色方块开始检查:

  • 0层开始,层深为0,采样深度图得到值为0.75,采样的结果大于层的深度,开始下一次迭代
  • 沿着V方向偏移纹理坐标,1层开始,层深为0.125,采样深度图的值为0.625,采样结果大于层的深度,开始下一次迭代
  • 沿着V方向偏移纹理坐标,2层开始,层深为0.25,采样高度图的值为0.4,采样结果大于层的深度,开始下一次迭代
  • 沿着V方向偏移纹理坐标,3层开始,层深为0.375,采样高度图的值为0.2,采样结果小于层的深度,所以得到了实际交点的近似点,采样本次使用的纹理坐标。

可以看出T3的坐标离实际交点还是有距离的,但是这种方式可以检查偏移采样的正确性,如果想得到更精确的结果,可以增加层的数量,提高采样的精确度。层数的提高会降低性能,降低层数会有明显的锯齿现象,可以根据视向量V和表面法向量N的夹角来动态决定层的数量。

vec2 ParallaxMapping(vec2 texCoords, vec3 viewDir)
{ //高度层数float numLayers = 5;//每层高度float layerHeight = 1.0 / numLayers;// 当前层级高度float currentLayerHeight = 0.0;//视点方向偏移总量vec2 P = viewDir.xy / viewDir.z * heightScale; //每层高度偏移量vec2 deltaTexCoords = P / numLayers;//当前 UVvec2  currentTexCoords     = texCoords;float currentHeightMapValue = texture(heightMap, currentTexCoords).r;while(currentLayerHeight < currentHeightMapValue){// 按高度层级进行 UV 偏移currentTexCoords += deltaTexCoords;// 从高度贴图采样获取的高度currentDepthMapValue = texture(depthMap, currentTexCoords).r;  // 采样点高度currentLayerHeight += layerHeight;  }return finalTexCoords;
}

浮雕视差映射 Relief Parallax Mapping

浮雕视差映射是在陡峭视差映射的基础上做出的升级优化,先进行陡峭视差映射,可以得到准确交点的前后两个层,和对应的深度值,然后在两层之间使用二分法进行迭代查找。

执行步骤:

  • ST、SH除以2,把纹理坐标T3沿着反方向偏移ST,深度沿反方向偏移SH,得到此次迭代的纹理坐标T4和深度H(T4)
  • * 采样高度图,ST、SH除以2
  • 如果采样高度图得到的深度值大于当前层的深度H(T4),将当前迭代层的深度增加SH,纹理坐标沿着V的方向偏移ST
  • 如果采样高度图得到的深度值小于当前层的深度H(T4),将当前迭代层的深度减去SH,纹理坐标沿着V的反向偏移ST
  • 从*处循环,知道达到规定的次数,或者两个深度偏差达到一个阈值
  • 得到的纹理坐标就是浮雕视差映射的结果

视差遮蔽映射 Parallax Occlusion Mapping

视差遮蔽映射是陡峭视差映射的另一个优化版本,浮雕映射使用二分法来提升精度,但是会降低性能。视差遮蔽性能比陡峭映射好,但是效果比浮雕映射要差。

视差遮蔽映射是使用陡峭映射得到的最后一次采样的H(T3)、UV和前一次采样的到的层深H(T2)、UV,进行一次插值的到的结果。

float2 ParallaxMapping(float2 texCoords, float3 viewDir)
{ //高度层数float numLayers = 50;//每层高度float layerHeight = 1.0 / numLayers;// 当前层级高度float currentLayerHeight = 0.0;//视点方向偏移总量float2 P = viewDir.xy / viewDir.z * _ParallaxStrength; //每层高度偏移量float2 deltaTexCoords = P / numLayers;//当前 UVfloat2  currentTexCoords     = texCoords;float currentHeightMapValue = tex2D(_ParallaxMap, currentTexCoords).r;while(currentLayerHeight < currentHeightMapValue){// 按高度层级进行 UV 偏移currentTexCoords += deltaTexCoords;// 从高度贴图采样获取的高度currentHeightMapValue = tex2Dlod(_ParallaxMap, float4(currentTexCoords,0,0)).r;  // 采样点高度currentLayerHeight += layerHeight;  }//前一个采样的点float2 prevTexCoords = currentTexCoords - deltaTexCoords;//线性插值float afterHeight  = currentHeightMapValue - currentLayerHeight;float beforeHeight = tex2D(_ParallaxMap, currentTexCoords).r - (currentLayerHeight - layerHeight);float weight =  afterHeight / (afterHeight - beforeHeight);float2 finalTexCoords = prevTexCoords * weight + currentTexCoords * (1.0 - weight);return finalTexCoords;
}

完成后的冰块效果:

知乎视频​www.zhihu.com

虽然完成了效果,但是对视差映射一直还是有一些没有搞懂的地方,为什么简单视差和陡峭视差在计算的时候偏移使用的是V.xy/V.z,而在带偏移上限的计算中偏移使用的是V.xy。在看过的很多文章中都是对所有视差映射的原理方法和代码进行了描述,并没有对这部分的讲解。

后来 @梦旅人 给我发了一篇文章:

https://catlikecoding.com/unity/tutorials/rendering/part-20/​catlikecoding.com

在这篇文章中详细的讲解了视差映射的相关内容,在这篇文章中找到了关于我的疑问的讲解,看过以后发现大家的文章中没有对于这部分的讲解估计是因为确实太基础了.....

从上图中可以看出,深度采样H的值肯定是在0-1之间的:

  • V.xy*H就可以得到一个一定范围内的偏移量,所以带偏移上限的视差映射使用的V.xy来计算偏移量的
  • 那为什么correct offset的偏移距离,也就是陡峭中的总偏移距离为什么是V.xy/V.z呢,将我们的数学功力发挥到初中水平就可以得到结果,∵ ∠a=∠b,∴ cot∠a=cot∠b,∴ xy/z = correct offset / 最大深度1,∴ correct offset = xy/z,这样xy分量除以z分量得到的就是总的UV坐标偏移量了........

结合陡峭的算法也就能想明白为什么要取UV的总偏移量.....

文章中部分内容、插图和代码参考和转自:

[译] GLSL 中的视差遮蔽映射(Parallax Occlusion Mapping in GLSL)​segmentfault.com吴洲:视差贴图(Parallax Mapping)学习笔记​zhuanlan.zhihu.com

梦旅人:Unity Shader基于视差映射的云海效果​zhuanlan.zhihu.com

GEngine:视差映射(Parallax Mapping)​zhuanlan.zhihu.com

视差图转为深度图_Parallax Mapping视差映射:模拟冰块相关推荐

  1. 视差图转换为深度图公司_视差刻录:使用SVG将照片从2D转换为3D

    视差图转换为深度图公司 Last week we talked about ZorroSVG, a tool for converting your chunky transparent PNG-32 ...

  2. 视差图转为深度图_纽劢研习社 | 深度图的非深度讲解

    本期话题:深度图 内容要点 1. 深度图基础知识 2. 不同的技术路径 传统方法 早期深度学习方法 当前深度学习方法 结合时序信息的深度学习方法 3. 深度估计的主要模块 对很多人来说,自动驾驶早已不 ...

  3. 立体匹配入门指南(8):视差图、深度图、点云

    本篇是比较简单的基础概念,刚入门的朋友可能是需要的. 视差图 三维点云 首先,我们要介绍下这三个概念. 视差(disparity) 视差 ddd 等于同名点对在左视图的列坐标减去在右视图上的列坐标,是 ...

  4. 双目立体视觉建立深度图_计算机视觉实验五 双目立体匹配获得视差图,深度图...

    完整源码链接 https://github.com/LamyaLi/cvLab 文章目录 一. 立体匹配的研究背景及意义 二. 立体匹配算法的基本实现思想 1.误差能量函数 2.基于最小平均误差能量的 ...

  5. 2019-9-29 opencv摄像机标定与三维重构4-Depth Map from Stereo Images立体图像中的深度图(视差图)

    官网参见https://docs.opencv.org/3.4.1/dd/d53/tutorial_py_depthmap.html 上一节中,我们学习了极线约束的概念和相关术语.主要包含:如果我们有 ...

  6. 计算机视觉——计算视差图

    计算视差图 1. 立体视差 2. 极线矫正 3. 归一化互相关(NCC) 4 . 计算视差图的步骤 5. 实验过程 5.1 实验代码 5.2 实验结果及分析 5.2.1 视差图计算结果 5.2.2 不 ...

  7. python 深度 视差 计算_python-窗口代价计算视差

    一.窗口代价计算视差的的基本原理 二.实现NCC视差匹配方法 三.图像集 四.总结 一.窗口代价计算视差的的基本原理 立体匹配算法的原理:就是找出两张图像的对应关系,根据三角测量原理,得到视差图:在获 ...

  8. OpenGL parallax mapping视差映射的实例

    OpenGL parallax mapping视差映射 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <glad/glad.h> # ...

  9. openMVS深度图计算:DenseReconstruction Estimate之EVTEstimateDepthMap之视差图初始化

    视差图初始化 1 对图像下采样 2 TriangulatePoints2DepthMap(对稀疏点三角化.栅格化.对depthMap进行插值) 3 三角网格栅格化 本文讲的是TSGM的视差图初始化,采 ...

最新文章

  1. Cable:360实现的新虚拟网络架构
  2. 少儿编程:玩游戏不如设计游戏!
  3. 成功解决未授予用户在此计算机上的请求登录类型图文教程
  4. ntp symmetric_Python使用示例设置symmetric_difference()方法
  5. STC51-l2C总线
  6. 涨价警告!2020年苹果新iPhone Pro系列售价或将万元起
  7. 【PAT (Basic Level) Practice (中文)】1029 旧键盘 (20分)
  8. redis客户端分析
  9. Linux 与 Windows 计算文件夹大小
  10. (译)如何制作一个类似tiny wings的游戏:第二部分(完) - 子龙山人 ...
  11. css中怎么设置字体加粗,css怎么把字体加粗加大
  12. 加密流量分类任务的深度学习方法(一般框架总结)
  13. 大连二级及二级以上医保定点医院名单
  14. 概率论与数理统计-------参数估计-------区间估计------置信区间、枢轴变量
  15. 冲量在线当选中关村数字经济产业联盟理事单位
  16. Java练习题-09
  17. 计算机二级-公共基础考点
  18. Contact form 7插件使用介绍和常见问题解答
  19. C++游戏服务器开发视频教程
  20. 用PPT制作点歌系统

热门文章

  1. wxWidgets:使用自定义对话框和 sizer
  2. wxWidgets:在对话框之间共享信息
  3. boost::multi_array模块实现在矩阵上测试切片
  4. boost::hana::sort用法的测试程序
  5. boost::math模块实现对贝塞尔函数的零点求和的测试程序
  6. boost::io::quoted用法的测试程序
  7. boost::histogram::indexed用法的测试程序
  8. boost::EccentricityProperty用法的测试程序
  9. Boost:双图bimap与lambda表达式的测试程序
  10. Boost:fork联接的测试程序