unity3d Human skin real time rendering 真实模拟人皮实时渲染(转)
先放出结果图片。。。由于网上下的模型是拼的,所以眼皮,脸颊,嘴唇看起来像 存在裂痕,解决方式是加入曲面细分和置换贴图 进行一定隆起,但是博主试了一下fragment shader的曲面细分,虽然细分成功了但是着色效果变的很奇怪,这里就不用曲面细分了,大家如果有在fragment shader上用曲面细分的好办法,可以的话请告诉我
参数设置1
参数设置2
细致到毛孔的高光
次表面散射的耳朵
人皮渲染是十多年的课题了,人们想尽一切办法想让其变得真实可信,大型3A级次时代游戏近来做的又来越真实了如《罗马之子》,他们的皮肤自称已经超过了NVIDIA的例子
这是在2005年SIGGRAPH的多层皮肤渲染,他们的参数都是经过精密的医学上的测量的,而且渲染花费了5分钟的时间。。。
研究这个找了许多资料,在结合之前的知识弄出了一个看起来还算入眼的人皮
本例做到了以下几点
1. 次表面散射
2. 基于物理的渲染
包括specular和brdf等等,brdf我用了一张贴图调整曲率来代替,specular在之前这篇文章有详细讲解链接在此
3. 法线模糊
等等之类。。。
为什么皮肤渲染这么难?
1. 大多数的漫反射光来自次表面散射
2. 皮肤颜色主要来自上表皮
3. 粉或红色主要为真皮中的血液
此图为人皮的组成模拟,人有好多层表皮,这就说明在真实情况下要进行数次折射与反射,这就更难达到真实
光的折射与反射
上图直观的表明了光线“被怎么样了”
光线接触到皮肤时,有大约96%被皮肤各层散射了,只有大约4%被反射
再说specular,人的皮肤会出油,所以就会有反射,但是人的皮肤不能像镜子那样反射,因为人的皮肤是粗糙的,在这种情况下用基于物理的(physically based)方法就最好不过了,没了解过physically based的看官们可以先了解下这篇文章和上面的一样
使用了之前试出效果最好的的方法,也就是使命召唤2中用到的的方法,同时试了下beckmann的方法,但是效果并不好,phong等方法也没有试
这里,我们的实现方法是这样的:
<span style="font-size:14px;"> /* *this part is compute Physically-Based Rendering *the method is in the ppt about "ops2" */ float _SP = pow(8192, _GL); float d = (_SP + 2) / (8 * PIE) * pow(dot(n2, H), _SP); float f = _SC + (1 - _SC)*pow(2, -10 * dot(H, lightDir)); float k = min(1, _GL + 0.545); float v = 1 / (k* dot(viewDir, H)*dot(viewDir, H) + (1 - k)); float all = d*f*v; float3 refDir = reflect(-viewDir, n2); float3 ref = texCUBElod(_Cubemap, float4(refDir, _nMips - _GL*_nMips)).rgb;</span>
然后发现尽管gloss调到最大也没有达到我们预期的那种效果,
又进行了 “智能补光”
也就是常规的求高光的方式,我们在此加入了高光贴图,不让不该高光的地方(如眉毛)产生高光
float specBase = max(0, dot(n2, H)); float spec = pow(specBase, 10) *(_GL + 0.2); spec = lerp(0, 1.2, spec); float3 spec3 = spec * (tex2D(_SpecularTex, i.uv_MainTex) - 0.1); spec3 *= Luminance(diff); spec3 = saturate(spec3); spec3 *= _SpecularPower;
光经过哪,就带一部分那里的颜色,可以发现光从入射到出射,位置和方向都变了
光走的路径数量是无穷大,光反射回来的都为漫反射,油脂表面的透明度也都是不一样的,
这就产生了次表面散射
NVIDIA在GDC2007年的演讲中提到把图像blur个六遍达到柔和的次表面散射效果
每次blur都是在不同的颜色通道以不同的范围和程度进行blur
由于我们的贴图是这样的“高配”
用在本例上会丢失少许本来贴图上的细节,但是确实有一定的次表面散射效果,各位看官自行取舍,而且千万不要只做高斯模糊,这样的话细节会丢失更多,而且没有什么次表面散射的感觉
为了节省花销,省去了ppt中的rendering时blur,直接在ps上做了6张高斯模糊的贴图放入material,并线性混合
float3 c = tex2D(_MainTex, i.uv_MainTex) * 128; c += tex2D(_BlurTex1, i.uv_MainTex) * 64; c += tex2D(_BlurTex2, i.uv_MainTex) * 32; c += tex2D(_BlurTex3, i.uv_MainTex) * 16; c += tex2D(_BlurTex4, i.uv_MainTex) * 8; c += tex2D(_BlurTex5, i.uv_MainTex) * 4; c += tex2D(_BlurTex6, i.uv_MainTex) * 2; c /= 256;
我们同时也起到重要作用的是边缘光rim和brdf,
使用了BRDF最明显的好处是,Brdf贴图间接控制了明暗交界线的颜色,可通过曲率控制,模拟了光与阴影交界处光对皮肤的反射与折射,如果全黑的话说明光只是普通的漫反射。
而且使人皮有了次表面散射的质感
/* *this part is to add the sss *used front rim,back rim and BRDF */ float3 rim = (1 - dot(viewDir, n2))*_RimPower * _RimColor *tex2D(_RimTex, i.uv_MainTex); float3 frontrim = (dot(viewDir, n2))*_FrontRimPower * _FrontRimColor *tex2D(_FrontRimTex, i.uv_MainTex); float3 sss = (1 - dot(viewDir, n2)) / 50 * _SSSPower; sss = lerp(tex2D(_SSSFrontTex, i.uv_MainTex), tex2D(_SSSBackTex, i.uv_MainTex), sss * 20)*sss; fixed atten = LIGHT_ATTENUATION(i); float curvature = length(fwidth(mul(_Object2World, float4(normalize(i.normal), 0)))) / length(fwidth(i.worldpos)) * _CurveScale; float3 brdf = tex2D(_BRDFTex, float2((dot(normalize(i.normal), lightDir) * 0.5 + 0.5)* atten, curvature)).rgb;
对于rim的话添加了前向rim和后向rim其实本质上还是rim,后向rim使用了白色的图片产生一种玉质的感觉(好吧其实更像羊羹),前向rim使用了红色的图片,相当于添加了光线在血液层的散射,让人的脸蛋有了真实的血色
这是次表面散射的结果:
光源在嘴里
像不像把手指或者耳朵放在手电筒前面的那种效果?那就是次表面散射
需要一张Intense strips贴图来混合原有颜色,方法就是在点光源情况下,求出当前点与点光源的距离,距离越近就越亮
关于法线,
用了一种新的混合方式,这样能保有更多法线细节,
这里简单讲解一下法线混合,
float3 n1 = tex2D(texBase, uv).xyz*2 - 1; float3 n2 = tex2D(texDetail, uv).xyz*2 - 1; float3 r = normalize(n1 + n2); return r*0.5 + 0.5;
大家可能用过这种方式来混合两个法线贴图,这种线性的方式折中了两个贴图,得到的细节权重是平均的,效果并不好,得到的是这样的结果
改进了一下,变成了覆盖混合
float3 n1 = tex2D(texBase, uv).xyz; float3 n2 = tex2D(texDetail, uv).xyz; float3 r = n1 < 0.5 ? 2*n1*n2 : 1 - 2*(1 - n1)*(1 - n2); r = normalize(r*2 - 1); return r*0.5 + 0.5;
就是法线1的法线比较深的地方,就多一些权重,比较浅的地方就被法线2适当覆盖,但是这样效果还是不够真实
在GDC2012的Mastering DX11 with unity中讲到了一种官方的办法如下:
float3x3 nBasis = float3x3(
float3(n1.z, n1.y, -n1.x), //绕着y轴+90度旋转
float3(n1.x, n1.z, -n1.y),// 绕着x轴-90度旋转
float3 (n1.x, n1.y, n1.z ));
n = normalize (n2.x*nBasis[0] + n2.y*nBasis[1] + n2.z*nBasis[2]);
得到的结果是这样的,是不是好了许多?
双方的细节程度都有很多提升,
他们用了一个basis来变换第二法线。具体可以看 这篇文章—链接
用
AutoLight.cginc里定义的一个函数LIGHT_ATTENUATION求出光的衰减atten,atten在directional
light中固定是1,在点光源中才有衰减效果,因为directional light在unity中是没有位置区别的,在哪里都一样。
fixed atten = LIGHT_ATTENUATION(i);
对于细节方面,如毛孔,在本例的贴图和法线贴图都很细致,已经包括毛孔和皮肤的纹路,如果贴图精度低还想要高细节的话,可以再贴上细节
全部可设置变量:
全部代码已共享至GitHub链接
unity3d Human skin real time rendering 真实模拟人皮实时渲染(转)的更多相关文章
- unity3d Human skin real time rendering 真实模拟人皮实时渲染
先放出结果图片...由于网上下的模型是拼的,所以眼皮,脸颊,嘴唇看起来像存在裂痕,解决方式是加入曲面细分和置换贴图 进行一定隆起,但是博主试了一下fragment shader的曲面细分,虽然细分成功 ...
- unity3d Human skin real time rendering with blood and water drop effect真实模拟人皮实时渲染之血液和水珠掉落效果
在之前的一篇(链接在此)文章中写了下关于真实模拟皮肤渲染,在此基础之上又想加上血液效果,在洗澡的时候(= =:)又想在skin上加上水珠的效果,所以研究了下,做出来效果感觉还不错,放下效果图: 水珠 ...
- unity3d Human skin real time rendering plus 真实模拟人皮实时渲染 plus篇
最近逃课做游戏,逃的有几门都要停考了,呵呵呵,百忙之中不忘超炒冷饭,感觉之前的人皮效果还是不够好,又改进了一些东西 首先上图 放大看细节 显而易见的比上次的效果要好很多,此次我把模型用3dmax进行了 ...
- Unity3d 屏幕空间人体皮肤知觉渲染&次表面散射Screen-Space Perceptual Rendering & Subsurface Scattering of Human Skin
之前的人皮渲染相关 前篇1:unity3d Human skin real time rendering 真实模拟人皮实时渲染 前篇2:unity3d Human skin real time ren ...
- 结合 CSS3 & Canvas 模拟人行走的效果
HTML5 和 CSS3 技术给 Web 带来了新的利器,点燃了 Web 开发人员的激情.所谓只有想不到,没有做不到,的确如此.下面给大家分享一个结合 CSS3 & Canvas 模拟人行走的 ...
- DirectUI中模态对话框和菜单的原理(自己控制整个Windows消息循环。或者,用菜单模拟窗体打开时用SetCapture取得控制权,一旦窗体收到WM_CAPTURECHANGED消息就把窗体退出)
经常有人问关于模态对话框和系统菜单内部实现原理方面的问题, 因为系统通过API隐藏了太多细节,这2个问题确实令初学者甚至是有经验的开发者困扰, 下面是我个人的一些经验总结. 先说模态对话框,外部看模态 ...
- python利用requests库模拟post请求时json的使用
我们都见识过requests库在静态网页的爬取上展现的威力,我们日常见得最多的为get和post请求,他们最大的区别在于安全性上: 1.GET是通过URL方式请求,可以直接看到,明文传输. 2.POS ...
- unity3d Hair real time rendering 真实头发实时渲染
先放上效果 惊现塞拉酱 算法是Weta Digital根据siggraph2003的论文加以改进,改进之前使用的是Kajiya and Kay’s 模型,它能量不守恒,也就是说不是基于物理的,不准确 ...
- unity3d Hair real time rendering 真实头发实时渲染(转)
惊现塞拉酱 算法是Weta Digital根据siggraph2003的论文加以改进,改进之前使用的是Kajiya and Kay’s 模型,它能量不守恒,也就是说不是基于物理的,不准确 电镜下真实头 ...
随机推荐
- Largest Rectangle in Histogram-最大长方形
题目描述: 给定n个非负整数height[n],分别代表直方图条的高,每个条的宽设为1,求直方图中面积最大的矩形的面积 题目来源: http://oj.leetcode.com/problems/la ...
- Git设置及GitHub的使用
把github上的help略略翻译一遍.备忘. First : 安装:ubuntu 下,终端输入命令: sudo apt-get install git-core git-gui git-doc Ne ...
- 蓝缘管理系统第三版推出。springMVC4.0+shiro1.2.3+spring4.x+Mybaits3.2.8
blog.csdn.net/mmm333zzz/article/details/42059349 http://blog.csdn.net/zoutongyuan/article/details/41 ...
- PHP 投票练习
重点:1.进度条的显示2.操作数据库<form action="chuli.php" method="post"> <?php include ...
- Java @override报错的解决方法
在eclipse中导入工程后@Overide报错 到入工程后,所有的@Override都报如下错误: Multiple markers at this line - The method getCou ...
- emplace_back减少内存拷贝和移动
--------<深入应用C++11:代码优化与工程级应用>第2章使用C++11改进程序性能,本章将分别介绍右值引用相关的新特性.本节为大家介绍emplace_back减少内存拷贝和移动. ...
- 【笨嘴拙舌WINDOWS】实践检验之按键精灵【Delphi】
通过记录键盘和鼠标位置和输入信息,然后模拟发送,就能够创建一个按键精灵! 主要代码如下: library KeyBoardHook; { Important note about DLL memory ...
- Codeforces 435 A Queue on Bus Stop
题意:给出n队人坐车,车每次只能装载m人,并且同一队的人必须坐同一辆车,问最少需要多少辆车 自己写的时候想的是从前往后扫,看多少队的人的和小于m为同一辆车,再接着扫 不过写出来不对 后来发现把每一队的 ...
- BZOJ 1911 特别行动队
另一个版本的斜率优化...这个要好理解一些. #include<iostream> #include<cstdio> #include<cstring> #incl ...
- BZOJ 4408 神秘数
题解同各神犇的方法... #include<iostream> #include<cstdio> #include<cstring> #include<alg ...