源码学习网 首页 编程学园 Unity3d教程 查看内容

基于物理的眼球折射效果

2019-7-16 22:00| 发布者: opiye| 查看: 67| 评论: 0

摘要: 前言:之前写过一篇基于Marmoset中的线性步进实现的眼球折射效果马甲:Marmoset眼球效果在Unity和UE4中的实现,虽然这种方法折射的效果很好,但是弊端很多,首先性能上,多重采样,还会打断GPU的高并行计算。制作流 ...
腾讯云服务器秒杀

前言:之前写过一篇基于Marmoset中的线性步进实现的眼球折射效果马甲:Marmoset眼球效果在Unity和UE4中的实现,虽然这种方法折射的效果很好,但是弊端很多,首先性能上,多重采样,还会打断GPU的高并行计算。制作流程上,双层模型结构,美术给角色的蒙皮会带来额外的工作量,所以进行优化折射的消耗和简化美术的制作流程。最终实现和上一篇效果对比如下

仔细看区别不大,虽然还是之前的折射效果更真实,但是放到手机上这部分的差距也可以忽略不计

优化实现:

1、模型合并:双模型换成单模型计算,利用双法线贴图,分别计算内外两层高光

2、折射修改:主要基于iryoku.com/downloads/Ne这篇ppt里的折射算法

有源码有图,而且仅仅就用了一张paper来阐述原理,本以为很简单就能理解,但是实际深入却卡在很多点,问了很多引擎大佬才搞明白整个流程!

折射是初中物理就学的内容,一开始奔着斯涅尔定律(Snell's Law)zh.wikipedia.org/wiki/%去梳理整个流程,最后发现在Real-Time Rendering 中有更快速的拟合公式来模拟折射

至于拟合推导的过程

Ray Tracing News, Volume 10, Number 1www.realtimerendering.com

有兴趣的可以看看,没兴趣的直接把公式拿来用,我属于后者。资料已经准备全了,接下来开始计算,首先refractedW的计算直接拿快速拟合的折射公式来进行推导,除了L部分其他都一模一样。

至于L的计算PPT里用的是预计算的方式将水平方向不同角度的L烘焙到一张3Dtexture里,由于3Dtexture,真的很占内存,所以我们索性用视线的向量来模拟灯光向量,这样反而更能模拟真实灯光的方向的折射,而不仅限于水平方向。

代码如下:

float w = _IOR * dot( normalW, i.viewW );float k = sqrt( 1.0 + ( w - _IOR ) * ( w + _IOR ) );float3 refractedW = ( w - k ) * normalW - _IOR * i.viewW;

至于cosAlpha是frontNormal和refacted两向量的夹角,用点积就可以直接计算出

[公式]

因为frontNormal和refacted都是归一化后的向量,所以

float cosAlpha = dot(_frontNormalW, -refractedW);//refacted向量要取一下反,这样才是这两向量的夹角,否则取到的是他俩的补角

利用等角转换可以知道height和refract的夹角值也是cosAlpha ,利用已知height可以算出refract的长度

float dist = height / cosAlpha;

有了长度和归一化后的refracted,可以算出refracted实际的向量

float3 offsetW = dist * refractedW;

这块儿我思考了好久,一开始不明白,算出来的明明是refracted的实际向量,按理说是三角形的斜边,怎么突然和直边等同了,最后终于想明白,一开始受最初那张图的迷惑,感官上觉得,平面上肯定会存在一个弧形透明膜,光线通过这个膜折射进去,然后进行采样!但是实际上并不存在,他就是一个平面,refracted就是等同offset!所以接下来一切就通了!接着用一张mask图来标注需要进行折射处理的区域!折射的完整代码如下:

                        fixed3 normalW = i.normal;		        float height = _anteriorChamberDepth * saturate( 1.0 - 18.4 * _radius * _radius ); 			// Refration			float w = _IOR * dot( normalW, i.viewW );    			float k = sqrt( 1.0 + ( w - _IOR ) * ( w + _IOR ) );    			float3 refractedW = ( w - k ) * normalW - _IOR * i.viewW;    			float mask = tex2D(_MaskTex,i.uv).r;    			fixed2 eyeUV = i.uv;    			float cosAlpha = dot(_frontNormalW, -refractedW);			float dist = height / cosAlpha;			float3 offsetW = dist * refractedW;			float2 offsetL =  mul(offsetW,(float3x2)unity_ObjectToWorld);			eyeUV += float2(mask,-mask) * offsetL;

3.功能性效果添加

改变颜色很简单这里就不说了,大概说一下瞳孔和虹膜的缩放,其实就是改变uv的区间范围,利用offset和scale的线性变换,让贴图的中心点始终保持在uv坐标系中间

[公式]

代码如下:

half2 offset = -0.5*(1 / _IrisSize) + 0.5;o.uv.xy = v.uv*(1 / _IrisSize) + offset;

Shader的核心部分就是这些,接下来是美术部分,所谓一个效果,三分靠着色器,七分靠贴图,贴图的制作直接影响最终的效果实现

懒得敲了,我直接把给美术写的制作规范贴上来了


后记:目前为止眼球的主要功能算是开发完了,但是后期肯定还会有迭代,比如说虹膜的形状图和瞳孔的形状图,其实就是一个灰阶图,为了可以程序化生成不同的颜色和形状,目前是单拎出来的,以后不同的形状可以合在一张图里面,假如单通道可以放3X3个形状,那么一张图可以支持27种,足够了吧!




来源网址:https://zhuanlan.zhihu.com/p/73268891

鲜花

握手

雷人

路过

鸡蛋