视差贴图和法线贴图都是使用特定的手段来达到欺骗视觉的目的,让人以为物体的表面是凹凸起伏的。而置换贴图却是真的将模型的顶点进行偏移,在原本的平面上创造出凹凸的效果。既然是对顶点进行偏移,那么就需要模型有足够多的顶点数量,否则达不到比较好的效果。为了达到足以置换的顶点数量,一般会使用 Tessellation 技术来增加低模的面数。查阅了相关资料,Tessellation 是 DirectX11 才有的技术,OpenGL 要到 4.0 才能使用。对于 Tessellation 知道的并不多,再加上移动平台根本无法使用,就没有去看这部分的内容,这里也不会涉及到。其实目前来看 Tessellation 技术并不是非用不可,很多情况下还是光照和贴图起到更大的作用。

看下要实现的效果,是一个转动的地球。地球的表面并不是平坦的,每一块陆地都是有凸起的,并且可以看到凸起的陆地上山脉和沟壑。球模型本身是 1200 面。

置换前效果 置换后效果 置换后效果

首先是在顶点阶段采样高度纹理,将采样到的颜色作为高度数据偏移顶点坐标(在顶点阶段采样纹理是sm3.0才支持的,我记得看到过什么资料上说 sm3.0 还是 2.0 是 DirectX 的叫法,和 OpenGL 没关系,不知道对不对。编译选项需要加上 #pragma glsl,让编译器把 cg 代码直接编译成 glsl,而不是中间代码,否则会报错)。可以看到如下效果。

// vertex shader
o.wnormal = mul (i.normal, (float3x3)_World2Object);
float dispValue = tex2D (_DisplacementMap, i.texcoord.xy).r * _DisplacementAmount;
o.wpos = mul(_Object2World, i.vertex);
o.wpos.xyz += o.wnormal * dispValue;
o.pos = mul(UNITY_MATRIX_VP, o.wpos);

地形表面已经有了凸起效果了。由于这里没有任何的光照计算,所以整体效果特别的平,没有体积感。到此为止还是很简单的,下面就是要加入光照计算。光照计算最重要的就是法线了,使用法线和光线进行计算来实现明暗效果,表现出物体的体积感。但是现在的问题是,由于模型表面已经不是平坦的了,而是凹凸不平的,如果还使用原来的法线信息肯定会得到错误的效果。

模型顶点的法线 置换顶点后错误的法线 置换顶点后正确的法线

只要能够计算出如右图所示的法线,再计算光照就不是什么难事了。我特意查看了下 Unity 文档中关于 Displacement Mapping 的代码,并在 Unity 编辑器中测试了下,光照效果是不正确的,因为当偏移顶点坐标的时候,并没有对法线进行调整。

从图中可以看到,左边的凸起是模型做出来的(可以看到网格),右边凸起是使用置换贴图实现的(看不到网格,使用的 Unity 文档中的代码实现)。问题就在于右边的光照是错误的,凸起的背光面并没有变暗,这正是因为偏移了顶点坐标后没有重新计算法线造成的。至于如何能够比较精确的计算出新的法线可以看这篇文章,作者对置换贴图进行了多次采样,通过计算高度差的方式实现的,感觉类似于 Unity 编辑器将灰度图转换成法线图的功能。然而我们这里使用另一种近似计算的方式,这种方式得到的结果并不是很精确,在模型边缘可能会出现瑕疵,但是计算量要小得多。我在设备上测试过,是可以得到比较好的效果。如果使用置换贴图后,光照效果的错误是可以忽略的,就不用调整法线了(和 Unity 一样,并且我看到很多类似的资料里也都没有讲到调整法线)。

下面就来说说如何来近似模拟计算调整后的法线。这里使用到的两个 cg 函数是 ddx 和 ddy。首先要说明的是 ddx 和 ddy 是在屏幕空间中的两个函数。如果写了这样的代码 ddx(x),得到的结果就是下一个像素(一般当前像素的右边一个像素)的 ddx(x) 括号中的 x 的值,减去当前像素 ddx(x) 括号中的 x 的值。也就是两个像素的 x 值相差多少。ddy同理,只是描述的是纵向,而不是横向。在旋转的地球这个例子中,如果下一个像素并不是地球模型,而是其他模型,那就可能出现上文所说的边缘瑕疵,如果你不是太在意,一般不会注意到。之所以这两个函数能够做到这点,是因为 GPU 中 fragment 是并行的。原理说明白了就看下代码。

float4 frag(v2f i)
{// 水平方向uv的增量float2 uv_dx = ddx(i.uv.xy);// 垂直方向uv的增量float2 uv_dy = ddy(i.uv.xy);// 当前置换距离float height = tex2D(_DisplacementMap, i.uv.xy).r;// 水平方向下一个像素点的置换距离float height_h = tex2D(_DisplacementMap, i.uv.xy + uv_dx).r;// 垂直方向下一个像素点的置换距离float height_v = tex2D(_DisplacementMap, i.uv.xy + uv_dy).r;// 水平方向置换增量float t_h = (height_h - height) * _DisplacementScale;// 垂直方向置换增量float t_v = (height_v - height) * _DisplacementScale;// 水平方向顶点坐标增量,作为一个假的tangent来使用float3 fake_tangent = ddx(i.wpos);// 水平方向顶点坐标增量,作为一个假的bintangent来使用float3 fake_bintangent = ddx(i.wpos);// 到这里为止,其实已经可以用 fake_tangent 差乘 fake_bintangent 来得到 fake_normal 了// 但是会发现 fake_normal 并不平滑,用来计算光照会出现硬边// 解决办法是使用从 vertex shader 传过来的 normal 来进行纠正,顶点上的 normal 是平滑的// corss 部分就是用平滑的 normal 来重新计算新的 fake_tangent// 后面加法是使用置换增量来对其进行扰动float3 fake_tangent_new = cross(fake_bintangent, i.wnormal) + i.wnormal * t_h;// 同理float3 fake_bintangent_new = cross(i.wnormal, fake_tangent) + i.wnormal * t_v;// 最终得到平滑的 fake_normalfloat3 fake_normal = cross(fake_bintangent_new, fake_tangent_new);fake_normal = normalize(fake_normal);// 这里就可以使用这个 fake_normal 参与光照计算了
}

至此,所有的要点都说明了。最后我又在设备上进行了测试,发现当图形接口是 OpenglES 3.0 的时候显示效果是正确的,但是 Metal 就不正确了。最终发现 Metal 中 ddy 和 OpenglES 3.0 中的 ddy 返回值不同,第一反应是 Metal 在实现上应该不会出问题,因为两个图形接口在 ddx 的返回结果是一致的。我把最后一步求平滑的 fake_normal 的差乘两边交换了一下,变成 float3 fake_normal = cross(fake_tangent_new, fake_bintangent_new),于是效果就正确了。这说明两个图形接口在处理 ddy 时,虽然都是纵向,但是方向相反。

goto blog

转载于:https://www.cnblogs.com/jim-game-dev/p/5476336.html

置换贴图 Displacement Mapping相关推荐

  1. 置换贴图(Displacement map),凹凸贴图(Bump map)与法线贴图(Normal map)的区别

    英文原文地址<Difference between Displacement , Bump and Normap Maps> By Pluralsight on August 14, 20 ...

  2. 【ZZ】 移位贴图 Displacement Mapping

    http://blog.csdn.net/huazai434/article/details/5650629 说明:该技术需要VS3.0的支持!!! 一,移位贴图类似于地形渲染.不过由于移位纹理可以做 ...

  3. 3DShader之移位贴图(Displacement Mapping)

    我们知道法线贴图是只是改了物体的法线属性,用来计算光照,但是并没有改变物体本身的网格.但是移位贴图就不一样了,它会移动物体的顶点.我用移位贴图做了个海洋,好了,上了图再讲: 注意看海的边缘的顶点,已经 ...

  4. 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)

    在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...

  5. 【3D建模制作技巧分享】Zbrush中凹凸贴图、法线贴图和置换贴图的区别

    我们现在的银幕早就充斥着电脑生成的虚拟物体,这些虚拟物体是电脑中生成的物体,但这些物体比较现实中的物体没有那么多细致的表面.因而需要用平面映射的方式加入更多的细节.这些用做映射的图片被称作贴图.贴图有 ...

  6. 教学|Zbrush中凹凸贴图、法线贴图和置换贴图的区别

    我们现在的银幕早就充斥着电脑生成的虚拟物体,这些虚拟物体是电脑中生成的物体,但这些物体比较现实中的物体没有那么多细致的表面.因而需要用平面映射的方式加入更多的细节.这些用做映射的图片被称作贴图.贴图有 ...

  7. 「建模学习」Zbrush中凹凸贴图、法线贴图和置换贴图的区别

    ZBrush中的三种修正虚拟三维物体表面形状的贴图分别是bump,normal和displacement,就是我们常说的凹凸贴图.法线贴图和置换贴图.这三个术语在现在的电影工业技术中也是很重要的概念. ...

  8. OpenGL 视差贴图 Parallax Mapping

    OpenGL 视差贴图 Parallax Mapping 视差贴图 Parallax Mapping简介 视差贴图 陡峭视差映射 视差遮蔽映射 视差贴图 Parallax Mapping简介 视差贴图 ...

  9. 灰度图,法线贴图,置换贴图和位移贴图

    查看全文 http://www.taodudu.cc/news/show-5684567.html 相关文章: OC置换可以混合.相乘.相加吗?多个置换贴图叠加 unity-shader-曲面细分与置 ...

最新文章

  1. 系统检测到您正在使用网页抓取工具_【安全】58反抓取简介
  2. 本地日志数据实时接入到hadoop集群的数据接入方案
  3. 获取不到时间_一月自考成绩查询时间一出,看了文章这次稳过
  4. Primefaces,Spring 4 with JPA(Hibernate 4 / EclipseLink)示例教程
  5. php和c语言那个竞争大,【后端开发】php和c语言哪个难
  6. manjaro配置zsh和oh-my-zsh
  7. 笔记本cpu排名_2020年双十一哪一款笔记本电脑值得买?高性价比笔记本电脑推荐(10月更新)...
  8. 【工大笔记】ACL20 如何使用选择机制提升自注意力网路能力?
  9. php在html中if,html里的if注释怎么使用
  10. CPC客户端报错 error
  11. Debian上如何打开关闭端口
  12. zuc算法代码详解_ZUC祖冲之序列密码算法
  13. Kmeans聚类算法应用实战
  14. 交换机ftp将文件传到服务器,如何用FTP实现交换机间配置文件复制?
  15. ffmpeg:制作gif / 提取视频帧为图片
  16. 计算机网络概念基础——分组交换
  17. Win10多用户远程桌面软件RDP Wrapper Library下载安装教程和解决Win10 1809(OS build17763)not supported问题
  18. 技术前沿与经典文章8:智能家居发展白皮书——全屋智能解决方案
  19. 支持向量机_6:Duality
  20. TF:jupyter notebook中plt.grid设置后不显示网格以及“TypeError: ‘bool‘ object is not callable”错误

热门文章

  1. ADG主备延迟时间的查询方法
  2. LIMS实验室管理系统的应用领域
  3. IntelliJ IDEA 下载安装及配置使用超详细图文教程
  4. php 数组转为字符,php 数组字符串如何转换为字符串
  5. 百度NLP面试记录(实习offered)
  6. 怎么样看待上海车展?
  7. 《Mastering the game of Go with deep neural networks and tree search》
  8. Unable to determine the device handle for GPU 0000:02:00.0: GPU is lost. Reboot the sys
  9. pytorch visualizer 深度神经网络可视化工具
  10. 我的世界java版怎么进不去存档_Java版进不去存档 #Bugjump#