zoukankan      html  css  js  c++  java
  • 崩坏3mmd中的渲染技术研究

    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。舞台平面都是用的镜面反射。

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

  • 相关阅读:
    windows系统历年高危漏洞
    安全产品分类
    防火墙
    UTM(统一威胁管理)
    ORA-39127: 调用 "WMSYS"."LT_EXPORT_PKG"."SCHEMA_INFO_EXP" 时发生意外错误
    oracle如何查看当前有哪些用户连接到数据库
    LINUX修改主机名
    删除Oracle用户及表空间
    Oracle AWR报告详细分析--比较详细
    RMAN优缺点及RMAN备份及恢复步骤
  • 原文地址:https://www.cnblogs.com/yangrouchuan/p/7544313.html
Copyright © 2011-2022 走看看