找回密码
 立即注册
查看: 382|回复: 0

unity 写实眼球shader实现

[复制链接]
发表于 2022-5-14 11:43 | 显示全部楼层 |阅读模式
主要参考了techiz大佬的实现方式,自己整理相关的思路,供各位参考。其中还是有一些不懂的地方,没有解决。后续希望可以攻破一下。
现实中眼球构造

首先我们了解一下人体眼睛的构造。



源自https://zhuanlan.zhihu.com/p/36819307

乍一看很复杂,但我们只需要关注几个关键的部位:虹膜(Iris)、巩膜(sclera)、瞳孔(pupil)、角膜(cornea)。作为业余人士,初步理解,虹膜表示了眼睛的有颜色部分,就是除去眼白的黑色大圆圈。瞳孔是在虹膜之内的黑色小圆圈。至于巩膜就是包裹整个眼球的最外层。在角膜与虹膜之间,有一个叫前房的东西,里面充满了液体。这也导致了我们看到的眼睛会有微微的折射感。



源自https://i1.meixingnan.com/cad6f6/918da049/c6d6f34d6c915c46dd2b.jpg

其次眼球的整体形状也是一个不规则的类似球体的样子,我们用两个球体的组合来比较合理的表示眼睛的构造。


整体框架:

模型

首先模型上采用一个近似半球形的模型,中间部分稍微凸出一部分。


UV部分为如下,这里有一点需要我们注意,我们为了方便计算,将UV的展开方式选择为了平面投影方式,uv范围从[0,1]重新映射到[-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,1],映射到[-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映射回[0,1]的正常范围中)。
虹膜缩放功能
这里只需要_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时,即计算瞳孔内部的范围。

可得


(new)uv为改变映射范围后的值。
另一种情况是计算的uv在瞳孔外边,即length(uv)大于瞳孔半径x
可得另一种方程


综合两种情况可得(new)uv的所有值(标量只是得出了length(uv)的大小)。
最后利用单位向量来获得正确的UV。
shader代码:


功能都制作完了,最后就用遮罩图对虹膜和巩膜做线性插值,得到结果。顺带高光利用Phong模型一起做了,再在Add pass做了一次高光。


其中有一句代码有点意思:


如果没有这if语句,实际上当虹膜缩小的时候,周围会出现一圈莫名的虹膜颜色东西,当_Distortion变大,周围一圈会越来越明显,同时在虹膜和那圈东西之间会出现BUG,有奇怪的投影,类似反射。



一圈奇怪的东西


后来根据实验,上述的if语句可以解决周围的圈圈问题,将虹膜和遮罩贴图修改为clamp模式,奇怪的投影就会消失。
<hr/>总结

效果图:






修改虹膜颜色、瞳孔大小、高光等参数

心得

总的制作下来,发现这套流程制作眼球的sahder消耗很大,不推荐使用。可以自己试着制作,拓宽思路。另外由于UV的特殊性,是通过Y轴的平面投影得到,限制了模型只能是一个类似的半球型,能够与圆形的UV匹配。不能应用在完整的球体模型上。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-16 10:47 , Processed in 0.089574 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表