unity 写实眼球shader实现
主要参考了techiz大佬的实现方式,自己整理相关的思路,供各位参考。其中还是有一些不懂的地方,没有解决。后续希望可以攻破一下。现实中眼球构造
首先我们了解一下人体眼睛的构造。
源自https://zhuanlan.zhihu.com/p/36819307
乍一看很复杂,但我们只需要关注几个关键的部位:虹膜(Iris)、巩膜(sclera)、瞳孔(pupil)、角膜(cornea)。作为业余人士,初步理解,虹膜表示了眼睛的有颜色部分,就是除去眼白的黑色大圆圈。瞳孔是在虹膜之内的黑色小圆圈。至于巩膜就是包裹整个眼球的最外层。在角膜与虹膜之间,有一个叫前房的东西,里面充满了液体。这也导致了我们看到的眼睛会有微微的折射感。
源自https://i1.meixingnan.com/cad6f6/918da049/c6d6f34d6c915c46dd2b.jpg
其次眼球的整体形状也是一个不规则的类似球体的样子,我们用两个球体的组合来比较合理的表示眼睛的构造。
整体框架:
模型
首先模型上采用一个近似半球形的模型,中间部分稍微凸出一部分。
UV部分为如下,这里有一点需要我们注意,我们为了方便计算,将UV的展开方式选择为了平面投影方式,uv范围从重新映射到[-0.5,0.5]。(实际上平面投影是错误的UV展开方式,贴图会有拉伸)
y轴平面投影得到的UV
贴图
准备了三张贴图,分别是虹膜遮罩图、巩膜贴图、虹膜贴图。
虹膜贴图:眼睛的虹膜部分,整个眼球的灵魂所在。贴图的效果基本决定了眼睛的质量。
巩膜贴图:包裹眼球的部分,包括血丝、眼白等细节都包含在里面。
虹膜遮罩图:通过遮罩将两张贴图叠加在一起,注意是有遮挡。不是颜色的数值叠加。
(如果不考虑使用遮罩,那么虹膜和巩膜的处理就是两层贴图的同时会显示,这种就是错误效果)
虹膜遮罩图
巩膜贴图
虹膜贴图
<hr/>
攻克难点:虹膜折射
为了在unity中达到现实中眼球的折射感。我们第一时间想到了视差映射的方法。通过偏移采样的UV位置,来达到折射效果。
重点处理思路:
数学部分
首先将虹膜当作平面,前房和角膜部分当作折射介质。为了方便计算我们将原UV的位置定在表面法线N和虹膜的交点处。现在需要原UV去产生一个偏移量来得到视差UV的位置。这样我们去看采样原UV的贴图,实际上得到是视差UV的位置,最终采样结果就会产生偏移,造成折射效果。
源自https://www.jianshu.com/p/9b7e40886ebf
我们已知原UV的情况,需要求得x的大小以及方向。
前提:V(视角)、N(表面法线)已知。
https://www.jianshu.com/p/9b7e40886ebf
计算过程如下:
其中n为折射率,定义是如下:入射角与折射角的正弦值之比,一般都是大于1的数值。例如水为1.33,水晶为1.55,金刚石为2.42,玻璃按成分不同而为1.5~1.9
https://baike.baidu.com/pic/%E6%8A%98%E5%B0%84%E7%8E%87/788655/1/a50f4bfbfbedab64713f01fcfe36afc379311eb8?fr=lemma&ct=single#aid=1&pic=a50f4bfbfbedab64713f01fcfe36afc379311eb8
接下来是方向的问题。偏移的x值的方向该如何定义。不难发现它的方向跟表面的法线方向有关。我们换个表面法线看,
偏移的方向又相反了。我就借鉴了techiz大佬使用的方法,其实我没有理解这里是怎么计算出来的。先拿来用了,发现只有这样虹膜的贴图采样,偏移的UV方向才是正确的。
shader部分
根据数学的计算公式。我们首先定义一些shader属性
我们先从这些属性知道,我们需要制作眼球的哪些功能
[*]眼球的折射(利用偏移UV完成)
[*]控制虹膜的缩放(通过_IrisRadius参数控制)
[*]控制瞳孔的缩放变化(通过_PupilRadius参数,涉及到UV重新映射范围)
折射功能
首先uv的范围需要从,映射到[-0.5,0.5]。
为了计算方便,我们将d的定义为两个球体之间的高度差,而不是表面法线在角膜和虹膜之间两个交点的线段长度。至于两个球体的关系,看下图。
这里就有一点要注意,两个球体的球心都要位于y轴中心,且都要经过(-0.5,0),(0.5,0)。这里我们将两球的交点范围其实刚好是UV重新映射后的范围[-0.5,0.5]。这样计算就没有偏差。首先大球的半径设为1,那么小球根据经验和实际模型大小判断,设为0.55.那么高度差d就可以如下表示(注意0.86603,0.22913等数值通过都要经过的交点(-0.5,0),(0.5,0)在球体方程中解算出来):
用d2-d1即可
我们一一将需要的数值算出带入即可得出偏移UV
计算所需数值
计算偏移UV含方向
接下来采样虹膜时,加上偏移UV就可以得到一个具有折射感的虹膜图。(这里需要提前将UV映射回的正常范围中)。
虹膜缩放功能
这里只需要_IrisRadius去控制sUV的大小。因为sUV的范围为[-0.5,0.5],相当于_IrisRadius刚好默认为0.5,要使UV在_IrisRadius没有被改变,即虹膜没有缩放。所以再除以2,使分母为1。
这样虹膜的缩放功能就已经实现了。实际上属性中_IrisRadius的值默认为0.225,是将虹膜默认缩小了。(可能是虹膜贴图制作出来就已经符合审美,但实际上应用在模型中偏大的,所以要被缩小采样)
瞳孔缩放功能
虹膜的整体大小不变,但映射的UV值要变。怎么理解呢?
我们需要一个映射关系,当瞳孔变化时,比如现在瞳孔半径1.4,UV此时范围也是[-0.5,0.5],UV的中心正好是圆圈的中心。
我们利用一维视角来看待问题:
这里的UV和(new)uv都是length(uv),相当于图中任意一点,以(0,0)为圆心,经过这一点的圆的半径长度,属于标量,后续会乘一个UV的单位方向向量。我们需要在下面的uv重新映射到上面的(new)uv范围中去。
假设瞳孔半径为x,考虑两种情况,1.在瞳孔内的uv值2.在瞳孔外的uv值
当计算的uv长度小于x时,即计算瞳孔内部的范围。
可得
https://www.zhihu.com/equation?tex=%5Cfrac%7Buv%7D%7Bx%7D%3D%5Cfrac%7B%EF%BC%88new%EF%BC%89uv%7D%7B1.4%7D
(new)uv为改变映射范围后的值。
另一种情况是计算的uv在瞳孔外边,即length(uv)大于瞳孔半径x
可得另一种方程
https://www.zhihu.com/equation?tex=%5Cfrac%7Buv-x%7D%7B0.5-x%7D%3D%5Cfrac%7B%EF%BC%88new%EF%BC%89uv-1.4%7D%7B3.6%7D
综合两种情况可得(new)uv的所有值(标量只是得出了length(uv)的大小)。
最后利用单位向量来获得正确的UV。
shader代码:
功能都制作完了,最后就用遮罩图对虹膜和巩膜做线性插值,得到结果。顺带高光利用Phong模型一起做了,再在Add pass做了一次高光。
其中有一句代码有点意思:
如果没有这if语句,实际上当虹膜缩小的时候,周围会出现一圈莫名的虹膜颜色东西,当_Distortion变大,周围一圈会越来越明显,同时在虹膜和那圈东西之间会出现BUG,有奇怪的投影,类似反射。
一圈奇怪的东西
后来根据实验,上述的if语句可以解决周围的圈圈问题,将虹膜和遮罩贴图修改为clamp模式,奇怪的投影就会消失。
<hr/>总结
效果图:
修改虹膜颜色、瞳孔大小、高光等参数
心得
总的制作下来,发现这套流程制作眼球的sahder消耗很大,不推荐使用。可以自己试着制作,拓宽思路。另外由于UV的特殊性,是通过Y轴的平面投影得到,限制了模型只能是一个类似的半球型,能够与圆形的UV匹配。不能应用在完整的球体模型上。
页:
[1]