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 模型,它能量不守恒,也就是说不是基于物理的,不准确 电镜下真实头 ...
随机推荐
- Java:IO流其他类(字节数组流、字符数组流、数据流、打印流、Properities、对象流、管道流、随机访问、序列流、字符串读写流)
一.字节数组流: 类 ByteArrayInputStream:在构造函数的时候,需要接受数据源,而且数据源是一个字节数组. 包含一个内部缓冲区,该缓冲区包含从流中读取的字节.内部计数器跟踪 read ...
- jenkins-slave的搭建和使用
一 什么是Jenkins的分布式构建和部署 Jenkins的分布式构建,在Jenkins的配置中叫做节点,分布式构建能够让同一套代码或项目在不同的环境(如:Windows和Linux系统)中编译.部署 ...
- (五)ASP.NET中动态生成控件
今天被问到如何在ASP.NET 页面中动态创建一批控件,并且希望在后续代码中能访问到这些动态创建的控件.我用下面的例子来解释这个问题 ================================= ...
- Android 判断用户2G/3G/4G移动数据网络
Android 判断用户2G/3G/4G移动数据网络 在做 Android App 的时候,为了给用户省流量,为了不激起用户的愤怒,为了更好的用户体验,是需要根据用户当前网络情况来做一些调整的,也可以 ...
- 自定义Eclipse的 “宏命令”
在Eclipse中有些常用的固定的东西可以定义一个“宏命令”,就不用重复地做一些工作.就像在 Preferences > Java > Code Style > Code Templ ...
- java-基础练习题1
/** 题目:古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,* 假如兔子都不死,问每个月的兔子总数为多少?* 1.程序分析: 兔子的规律为数列1, ...
- HeadFirst 12 (web应用安全)
测试领域可以使用的方法,在tomcat-users.xml 中设置授权, 为什么这个不能在生产环境中呢, 因为如果这个设置在生产环境中, 那么当你想修改”授权时”, 就要修改这个xml文件, 那么就要 ...
- jsonp获取服务器数据的方式
jsonp获取服务器的数据,有两种 一,跨域 二,不跨域 如果跨域 js的写法有两种 1, <script type="text/javascript"> $(func ...
- jquery ajax 开发手记
1.json解析的格式要求更严格了,必须全部加引号,否则无法识别 {"result":"false"} 2.ashx如果要使用Session需要继承接口IReq ...
- c#调用系统资源大集合-1
using System; using System.Collections.Generic; using System.Text; using System.Runtime.InteropServi ...