zoukankan      html  css  js  c++  java
  • Unity酱~ 卡通渲染技术分析(一)

    前面的话

    unitychan是日本unity官方团队提供的一个Demo,里面有很好的卡通渲染效果,值得参考学习

    Shader分类

    上图是我整理出来的shader结构,可以看到Unity娘被拆分成了很多个小的部件,我想主要是为了挂动态骨骼吧。因为有很多部件的材质,shader其实都是一样的可以合并成少数几个

    我打算分3个部分来学习

    1. CharaMain.cginc 主要用于衣服等材质
    2. CharaSkin.cginc 皮肤效果
    3. Hair 头发、眼睛、睫毛等部位的渲染

    CharaMain.cginc

    本篇先写第一部分body材质。CharaMain.cginc中包含漫反射、高光、反射、边缘光、阴影等效果的实现,接下来我们详细拆解

    基础着色效果(模拟漫反射)

    这里是用视角向量跟法线点积(但这样的做法就不会受光照角度变化),然后用结果采样一张类似这样的衰减纹理

    	// Falloff. Convert the angle between the normal and the camera direction into a lookup for the gradient
    	float_t normalDotEye = dot( normalVec, i.eyeDir.xyz );
    	float_t falloffU = clamp( 1.0 - abs( normalDotEye ), 0.02, 0.98 );
    	float4_t falloffSamplerColor = FALLOFF_POWER * tex2D( _FalloffSampler, float2( falloffU, 0.25f ) );
    	float3_t shadowColor = diffSamplerColor.rgb * diffSamplerColor.rgb;
    	float3_t combinedColor = lerp( diffSamplerColor.rgb, shadowColor, falloffSamplerColor.r );
    	combinedColor *= ( 1.0 + falloffSamplerColor.rgb * falloffSamplerColor.a );
    

    FalloffSampler
    效果如下,边缘较白
    漫反射

    但我感觉加上采样颜色后效果不是很明显,边缘颜色会深一些。
    加上采样颜色

    高光效果

    	// Use the eye vector as the light vector
    	float4_t reflectionMaskColor = tex2D( _SpecularReflectionSampler, i.uv.xy );
    	float_t specularDot = dot( normalVec, i.eyeDir.xyz );
    	float4_t lighting = lit( normalDotEye, specularDot, _SpecularPower );
    	float3_t specularColor = saturate( lighting.z ) * reflectionMaskColor.rgb * diffSamplerColor.rgb;
    	combinedColor += specularColor;
    

    这个高光的计算很奇葩,也是用法线跟视角向量的点积,最后的效果就是有一点淡淡的高光。也是跟真正的灯光角度无关的。

    这里还用到了一张贴图,高光会用到这张图的rgb通道,后面要写的反射,会用到这张图的a通道。
    大概就是描出来那些地方高光强一些

    RGB通道

    高光效果

    反射

    // Reflection
    float3_t reflectVector = reflect( -i.eyeDir.xyz, normalVec ).xzy;
    float2_t sphereMapCoords = 0.5 * ( float2_t( 1.0, 1.0 ) + reflectVector.xy );
    float3_t reflectColor = tex2D( _EnvMapSampler, sphereMapCoords ).rgb;
    reflectColor = GetOverlayColor( reflectColor, combinedColor );
    
    combinedColor = lerp( combinedColor, reflectColor, reflectionMaskColor.a );
    combinedColor *= _Color.rgb * _LightColor0.rgb;
    float opacity = diffSamplerColor.a * _Color.a * _LightColor0.a;
    

    这里是采样一张环境贴图,为啥用张这样的图呢?我觉得他主要是为了能反射出这种银色的色调罢了。你想要什么色调就换啥样的环境图。

    GetOverlayColor这个函数是用来融合自身贴图颜色,跟反射环境贴图颜色的。里面用了很多小技巧

    先来看看直接输出反射贴图是什么样

    反射贴图

    通过GetOverlayColor融合后的反射颜色
    融合后

    然后这里会用到高光反射贴图的A通道,来表示某些区域的反射的强度。
    Alpha通道

    用alpha通道做差值后会发现,大部分区域的反射颜色都不见了,因为大部分是黑色。只有少数白色区域,能看到一些反射效果
    最终反射效果

    接收阴影处理

    这里是计算其他物体投射在身上的阴影,这里插入了一个自定义的阴影颜色,为了明显一点我直接调成纯黑,然后弄了一个物体挡住了unity酱~ 效果如图

    使用LIGHT_ATTENUATION指令对阴影贴图采样并且返回数据供你使用。如果你想知道LIGHT_ATTENUATION指令具体做了些什么,检查 AutoLight.cginc文件

    #ifdef ENABLE_CAST_SHADOWS
    	// Cast shadows
    	shadowColor = _ShadowColor.rgb * combinedColor;
    	float_t attenuation = saturate( 2.0 * LIGHT_ATTENUATION( i ) - 1.0 );
    	combinedColor = lerp( shadowColor, combinedColor, attenuation );
    #endif
    

    边缘高光

    终于这里有一个跟着光照角度变换的效果了,哈哈

    一般我们会使用菲尼尔效应来实现边缘光,而unitychan里面则是采用了一张边缘光贴图

    RimLight

    用N.L计算出来的值域在[-1,1]之间,*0.5+1后,转成[0,1]区间,然后对这张1维的rim图采样。

    我们从右边打一盏平行灯,来看看采样的效果。跟光向量夹角越小的值越接近1,也就越白

    边缘光贴图采样

    但是边缘光效果要收到漫反射影响,所以他这里乘上了之前采样出来的漫反射渐变,可以看到高光被控制在了边缘

    falloffU = saturate( rimlightDot * falloffU );
    

    rim * 漫反射渐变

    完整效果

    完整效果

    总结

    CharaMain.cginc中用了很多的小技巧实现了漫反射、高光、反射、边缘光。她没有传统卡通渲染赛璐璐的阴影渐变,也没有油腻的高光。她的效果都有种欲出还收的感觉。

    我还是觉得效果太淡了一些

  • 相关阅读:
    模拟器安装.apk包_夜神模拟器
    SDK安装报错_2019
    Jenkins安装插件方法
    Jenkins安装
    Python项目第三方库安装_pip freeze命令
    深入理解Java虚拟机—内存分配
    深入理解Java虚拟机—垃圾回收 下
    深入理解Java虚拟机—垃圾回收 上
    深入理解Java虚拟机—OutOfMemoryError异常
    深入理解Java虚拟机—Java内存区域
  • 原文地址:https://www.cnblogs.com/lijiajia/p/12332232.html
Copyright © 2011-2022 走看看