http://youxiputao.com/articles/11839 主要是参考该篇文章做一个微小的复盘。

漫反射与高光

文章中的漫反射与高光并不是类似于普通的

resultCol = Diffuse(...) + Specular(...)

而是更贴近实际的cg绘画流程中的,基色+阴影色+高光的模式。

其中阴影色以正片叠底的方式叠加到基色上,之后再加上高光

这也是为什么文章中的DiffuseRamp跟SpecularRamp的两张贴图方向相反。因为Diffuse是作为阴影上色,因此其Ramp贴图在照度低的左部颜色更亮。

同时这种上色也使得传统的forward rendering中的复数光源用blend one one进行渲染变得不可能了(我猜的),因此只有一个主要的directional light作为光源。不过这样也够用了。

这里涉及到的正片叠底的模式,其实就是原有色与叠加色直接相乘。叠加色为1(白)时,结果不变,叠加色为0时变黑。文章中不止考虑了叠加的颜色,还考虑了叠加的强度(alpha通道)。此时叠加的代码如下:

//_DiffuseLayer1是叠加色,diffuse是在diffuseRamp上采样的结果
resultCol *= lerp(_DiffuseLayer1.rgb, fixed3(1, 1, 1), 1 - (_DiffuseLayer1.a * diffuse));

在cg绘画流程中,阴影图层往往不止一个,而是有多个来提升表现力。在文章中提到,DiffuseRamp的三个通道是分开使用进行上色的。设计师通过修改对应三个通道的Tint色来进行颜色的调整。此时代码如下:

fixed3 diffuse = tex2D(_DiffuseRamp, float2(diffuseAtten,0));
fixed3 white = fixed3(1, 1, 1);
resultCol *= lerp(_DiffuseLayer1.rgb, white, 1 - (_DiffuseLayer1.a * diffuse.r));
resultCol *= lerp(_DiffuseLayer2.rgb, white, 1 - (_DiffuseLayer2.a * diffuse.g));
resultCol *= lerp(_DiffuseLayer3.rgb, white, 1 - (_DiffuseLayer3.a * diffuse.b));

specular的部分和这个类似,区别在于不是正片叠底,而是直接相加。

软硬风格切换没什么好说的,根据采样到的顶点色去修改diffuseRamp的采样位置即可。

文章中提到的降低面部阴影的操作,我猜测是用了pow函数,将亮度采样整体提升,同时也可以保证亮度为0、1时不受干扰。

diffuseAtten = saturate(pow(diffuseAtten, 1 - 0.95 * _ShadowMask));	//so shadow will be less when shadowmask is high.

边缘光

不同于普通的边缘光,加入了对环境的采样。代码如下

half3 Fresnel(half3 normal, half3 viewDir,half exponent) {
//fresnel(rim light)
half fresnel = 1 - DotClamped(normal, viewDir);
fresnel = saturate(pow(fresnel, exponent));
half3 reflectionDir = reflect(-viewDir, normal);
float4 envSample = UNITY_SAMPLE_TEXCUBE(unity_SpecCube0, reflectionDir);
float3 envLight = DecodeHDR(envSample, unity_SpecCube0_HDR);
return envLight * fresnel;
}

眼睛折射

最简单的眼球渲染的模型就是一个圆形。

但是考虑到人眼的虹膜部分并不是跟眼球一个表面,而是内凹的一个形状,因此正确的渲染是虹膜部分向内部凹陷一定距离。

另外,虹膜到眼球表面的这段距离会造成一定的折射效果。但是这个折射效果跟凹陷效果其实不太容易分辨的出来,可以当做一种效果处理。

具体的实现就是根据视线方向去计算uv偏移。我们可以输入一个深度贴图,表示某个位置距离眼球表面的距离。

偏移的计算参考自 https://computergraphics.stackexchange.com/questions/4133/eye-parallax-refraction

//choose one to calculate uv offset(or use both)
float2 ReflectOffset(float2 ouv,half3 V) {
fixed depth = tex2D(_EyeDepthMask, ouv);
float2 offset = depth * mul((float2x3)unity_WorldToObject, V);
offset.y = -offset.y;
return ouv + offset * _EyeCenter.z;
} float2 PBReflection(float2 ouv, half3 V, half3 frontW, half3 refractedW) {
half depth = tex2D(_EyeDepthMask, ouv).r * _EyeCenter.w;
float cosAlpha = dot(frontW, -refractedW);
float dist = depth/cosAlpha;
float3 offsetW = dist * refractedW;
float2 offsetL = mul((float2x3)unity_WorldToObject, offsetW);
offsetL.y = -offsetL.y;
return ouv + offsetL;
}

眼睛焦散

跟文章中提到的类似,根据反射后的lightDir计算一个diffuseterm,加上去就行了。

		//caustic
half3 backLightDir = normalize(reflect(-lightDir, eyeForward));
half causticAtten = pow(DotClamped(backLightDir, normal),3);
half3 caustic = _EyeCausticTint * causticAtten * _LightColor0.rgb * s.EyeCaustic; //s.EyeCaustic是Caustic贴图的采样
resultCol += caustic;

头发高光

同时顶部的文章中的图片已经给出了代码。需要注意的是根据uv方向的不同,tangent可能要换成bitangent。

加入JitterMap后做抖动:

		float tangentShift = tex2D(_SpecularJitterMap, IN.uv_SpecularJitterMap);
o.tangent += tangentShift * IN.worldNormal * _SpecularJitterStrength;
o.tangent = normalize(o.tangent);

文章中提到了两层高光,一层低频一层高频。可以配合SpecularRamp的两个通道去做,这样最终的效果会更贴近绘画的风格。

参考: http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2012/10/Scheuermann_HairSketchSlides.pdf

镜面反射

参考 http://gad.qq.com/article/detail/18544。

其中文末提到的bug可以通过斜投影解决,斜投影矩阵的计算可以参考http://gad.qq.com/article/detail/18612。

Unity内置了计算斜投影的函数,可以不用操心行列向量左右手坐标系之类琐碎的事情,具体的实现可以参考http://wiki.unity3d.com/index.php/MirrorReflection4。

屏幕空间反射

个人觉得挺有意思的效果,从头学了一下,重新实现了一遍,在 http://www.cnblogs.com/yangrouchuan/p/7574405.html

有个问题就是无法反射Forward rendering的物体。视频中似乎是只在远离人物的地方使用SSR。舞台平面都是用的镜面反射。

剩下的日后慢慢更新,目前还太菜了。。。

崩坏3mmd中的渲染技术研究的更多相关文章

  1. Unity Shader入门精要学习笔记 - 第16章 Unity中的渲染优化技术

    转自冯乐乐的 <Unity Shader 入门精要> 移动平台的特点 为了尽可能一处那些隐藏的表面,减少overdraw(即一个像素被绘制多次),PowerVR芯片(通常用于ios设备和某 ...

  2. 基于.net的分布式系统限流组件 C# DataGridView绑定List对象时,利用BindingList来实现增删查改 .net中ThreadPool与Task的认识总结 C# 排序技术研究与对比 基于.net的通用内存缓存模型组件 Scala学习笔记:重要语法特性

    基于.net的分布式系统限流组件   在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可 ...

  3. geotrellis使用(三十四)矢量瓦片技术研究——矢栅一体化

    前言 本文所涉及技术与Geotrellis并无太大关系,仅是矢量瓦片前端渲染和加载技术,但是其实我这是在为Geotrellis的矢量瓦片做铺垫.很多人可能会说,Geotrellis为什么要搞矢量瓦片, ...

  4. 基于Unity 5的次世代卡通渲染技术 -- Unite 2017 米哈游总监贺甲分享实录

    在5月12日Unite2017开发者大会上,米哈游技术总监兼美术指导贺甲进行了主题为次世代卡通渲染的演讲.一下为详细分享内容: 大家好,首先自我介绍一下,我叫贺甲,在米哈游担任技术总监和美术指导工作, ...

  5. 剖析Unreal Engine超真实人类的渲染技术Part 1 - 概述和皮肤渲染

    一.概述 1.1 数字人类的概要 数字人类(Digital Human)是利用计算机模拟真实人类的一种综合性的渲染技术.也被称为虚拟人类.超真实人类.照片级人类. 它是一种技术和艺术相结合的综合性模拟 ...

  6. 剖析Unreal Engine超真实人类的渲染技术Part 2 - 眼球渲染

    目录 三.眼球渲染 3.1 眼球的构造及理论 3.1.1 眼球的构造 3.1.2 眼球的渲染理论 3.2 眼球的渲染技术 3.2.1 角膜的半透和光泽反射 3.2.2 瞳孔的次表面散射 3.2.3 瞳 ...

  7. 剖析Unreal Engine超真实人类的渲染技术Part 3 - 毛发渲染及其它

    目录 四.毛发渲染 4.1 毛发的构造及渲染技术 4.1.1 毛发的构造 4.1.2 Marschner毛发渲染模型 4.1.3 毛发的间接光照 4.2 毛发的底层实现 4.3 毛发的材质解析 4.3 ...

  8. 三维场景中使用BillBoard技术

    三维场景中对于渲染效果不是很精致的物体可以使用BillBoard技术实现,使用该技术需要将物体实时朝向摄像机,即计算billboard的旋转矩阵M. 首先根据摄像机位置cameraPos和billBo ...

  9. Web 开发中应用 HTML5 技术的10个实例教程

    HTML5 作为下一代网站开发技术,无论你是一个 Web 开发人员或者想探索新的平台的游戏开发者,都值得去研究.借助尖端功能,技术和 API,HTML5 允许你创建响应性.创新性.互动性以及令人惊叹的 ...

随机推荐

  1. python学习笔记01:安装python

    下载python: 从从https://www.python.org/downloads/下载python,根据操作系统的不同,选择不同的版本下载.注意:linux系统大多预装了python,可以直接 ...

  2. IPReversePathFilter

    nstat TcpExtIPReversePathFilter for i in /proc/sys/net/ipv4/conf/*/rp_filter ; do > echo 0 > $ ...

  3. MYsql 数据库密码忘记(Window)-2(mysql 5.7)

    很久没用Mysql了,再次打开,发现用不了了,密码忘了,服务也无法打开,在cmd中输入mysql之后,显示不是内部指令. 看来问题是mysql服务打不开了 (1)在cmd中 输入net start m ...

  4. BIO、NIO、AIO通信机制

    一.BIO的理解 首先我们通过通信模型图来熟悉下BIO的服务端通信模型:采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端的连接请求之后为每个客户端创 ...

  5. 【EF】EF实现大批量数据库插入操作

    通过参考http://www.itnose.net/news/171/6306259里面介绍的方法,我们可以在程序的解决方案的引用中选择NuGet管理包中添加  该扩展方法提供BulkInsert() ...

  6. Codeforces Round #522 Div. 1 没打记

    开场被A劝退,写了得有50min于是不敢交了.unrated了喜闻乐见. A:瞎猜都能猜到如果要走到那条直线上,进入直线的点横坐标或纵坐标与起点相同,离开直线的点横坐标或纵坐标与终点相同,证明脑补一下 ...

  7. BZOJ4804 欧拉心算(莫比乌斯反演+欧拉函数+线性筛)

    一通套路后得Σφ(d)μ(D/d)⌊n/D⌋2.显然整除分块,问题在于怎么快速计算φ和μ的狄利克雷卷积.积性函数的卷积还是积性函数,那么线性筛即可.因为μ(pc)=0 (c>=2),所以f(pc ...

  8. Greenlet-手动切换

    yield()是自己写的协程,Greenlet( )是已经封装好了的协程. 协程:遇到 I/O 操作就切换到别的地方了(先去处理其他携程去了).等原协程的 I/O 操作一完成就切回去.这样就把 I/O ...

  9. BZOJ4553:[HEOI2016/TJOI2016]序列——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4553 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他.玩具上有一个数列,数列中某 ...

  10. 【简单算法】17.字符串转整数(atoi)

    题目: 实现 atoi,将字符串转为整数. 在找到第一个非空字符之前,需要移除掉字符串中的空格字符.如果第一个非空字符是正号或负号,选取该符号,并将其与后面尽可能多的连续的数字组合起来,这部分字符即为 ...