8.8 KiB
资料整理
凹模型
直接贴图即可。 折射 模型高光
凸模型
- 使用视差贴图来控制瞳孔效果 + 折射模拟
float2 viewL = mul(viewW, (float3x2) worldInverse);
float2 offset = height * viewL;
offset.y = -offset.y;
texcoord -= parallaxScale * offset;
- Physically based refraction
// 角膜区域突起的模型
// Alternatively, use a displacement map
// height = max(-positionL.z – eyeIrisDepth, 0.0);
// 球形模型
// Plot[Max[1.0 - 18.4 * r * r, 0.0], {r, 0, 0.3}]
height = anteriorChamberDepth * saturate( 1.0 - 18.4 * radius * radius );
// refractedW
float w = n * dot( normalW, viewW );
float k = sqrt( 1.0 + ( w - n ) * ( w + n ) );
float3 refractedW = ( w - k ) * normalW - n * viewW;
float cosAlpha = dot(frontNormalW, -refractedW);
float dist = height / cosAlpha;
float3 offsetW = dist * refractedW;
float2 offsetL = mul(offsetW, (float3x2) worldInverse);
texcoord += float2(mask, -mask) * offsetL;
代码中首先计算了height,即前房的高度,PPT中height有两种计算方式,分别对应两种眼睛的模型结构,对应的结构写在注释中了。 然后计算了refracted,这个是rtr中快速拟合的计算方法,n是空气与介质折射率的比值,关于refracted的推论可以参考: YivanLee:虚幻4渲染编程(人物篇)【第三卷:Human Eye Rendering】 129 赞同 · 12 评论文章
最后一段,先通过frontNormalW与refractedW的点积计算出α角的cos值(上图中的α应该是标识错误,α是-refractedW与frontNormalW的夹角)。然后已知height,通过比值可以计算出refractedW的模长dist。offsetW即为完整的refractedW向量。最后转换到本地空间,乘上眼睛的Mask,加到原本的UV上。 之后就是使用偏转后的UV去采样贴图了。 与视差相同,这里也是在本地与世界空间中进行的计算,同样会有轴向问题,主要是normalW、viewW和frontNormalW参与的计算,normal与view可以转换到切线空间计算,而frontNormalW代表的是模型向前的朝向,这个必须要指定,不过图方便的话,把frontNormalW改成切线空间法线也不是不可以。
多层复合模型
樱花大战cedec2020分享:https://blog.ch-wind.com/cedec2020-new-sakura-wars-note/
瞳孔的高光叠加在其突出的半透明部分上。根据摄像角度的不同,各个部分的贴图会分开进行移动,使得在哪个角度高光都能处在一个刚好的位置。 控制上,有针对高光上下左右的移动强度与控制移动范围的参数共同作用。
从左边开始,是作为基础颜色的Albedo,以及用于Mask瞳孔的Alpha贴图,用于在Albedo上进行叠加的spt贴图,以及两张瞳孔高光,以及反应环境的matcapture贴图。 虽然很多动画风格的渲染中会省略掉瞳孔中的虹彩部分,但是本作为了提高角色靠近时的效果,进行了详细的绘制,同时为了体现环境的变化与matcap的贴图进行叠加。 高光贴图有两张,分别使用不同的UV动画进行控制,用于表现眼睛的湿润感。虽然是很细微的操作,但是对于表现角色的感情非常的有用。
其他效果实现
眼睛高光效果
- 贴图高光。使用事先绘制的高光形状贴图,贴到最外面的。并且使用ViewDirection来控制。设定4个UV Coord, 根据 View=》眼睛的本地坐标系=》Normalize后的向量进行插值。
PBR的思路
Matcap反射效果
Matcap材质+球形法线贴图
float3 NormalBlend_MatcapUV_Detail = viewNormal.rgb * float3(-1,-1,1);
float3 NormalBlend_MatcapUV_Base = (mul( UNITY_MATRIX_V, float4(viewDirection,0)).rgb*float3(-1,-1,1)) + float3(0,0,1);
float3 noSknewViewNormal = NormalBlend_MatcapUV_Base * dot(NormalBlend_MatcapUV_Base, NormalBlend_MatcapUV_Detail) / NormalBlend_MatcapUV_Base.b - NormalBlend_MatcapUV_Detail;
float2 ViewNormalAsMatCapUV = noSknewViewNormal.rg * 0.5 + 0.5;
焦散效果
焦散的表现反倒简单了,直接画在眼睛贴图上都可以,考虑到卡通表达的自由性,焦散是否存在与焦散的形状都可以没有限制,只要好看就行。 下图也是miHoYo的分享,可以简单的理解为直接贴张Mask上去,然后用光照方向和菲涅尔去影响强度变化。
使用Mask贴图、NoL与菲尼尔来控制
其他参考整理
PBR Refrection(有问题)
//input anteriorChamberDepth 贴图 使用HeightMap
//input radius
//input mask 贴图
//input texcoord
//input n 折射率比值
//input frontNormalW 贴图float3(0,0,1) Local=>World
// 角膜区域突起的模型 height = max(-positionL.z – eyeIrisDepth, 0.0);
// 球形模型 Plot[Max[1.0 - 18.4 * r * r, 0.0], {r, 0, 0.3}]
float height = anteriorChamberDepth ;//* saturate( 1.0 - 18.4 * radius * radius );
float3 normalW=Parameters.WorldNormal;
float3 viewW= mul(float3(0,0,1),(float3x3)View.ViewToTranslatedWorld);//CameraViewToTranslatedWorld
// refractedW
float w = n * dot( normalW, viewW );
float k = sqrt( 1.0 + ( w - n ) * ( w + n ) );
float3 refractedW = ( w - k ) * normalW - n * viewW;
refractedW=-normalize(refractedW);
//float3 frontNormalW=mul(float(0,0,1),float(3x3)GetPrimitiveData(Parameters.PrimitiveId).LocalToWorld) * -1;
float cosAlpha = dot(frontNormalW, refractedW);
float dist = height / cosAlpha;
float3 offsetW = dist * refractedW;
float2 offsetL = mul(offsetW, (float3x2)GetPrimitiveData(Parameters.PrimitiveId).WorldToLocal);
texcoord += float2(mask, -mask) * offsetL;
return texcoord;
原始代码
// 角膜区域突起的模型
// Alternatively, use a displacement map
// height = max(-positionL.z – eyeIrisDepth, 0.0);
// 球形模型
// Plot[Max[1.0 - 18.4 * r * r, 0.0], {r, 0, 0.3}]
height = anteriorChamberDepth * saturate( 1.0 - 18.4 * radius * radius );
// refractedW
float w = n * dot( normalW, viewW );
float k = sqrt( 1.0 + ( w - n ) * ( w + n ) );
float3 refractedW = ( w - k ) * normalW - n * viewW;
float cosAlpha = dot(frontNormalW, -refractedW);
float dist = height / cosAlpha;
float3 offsetW = dist * refractedW;
float2 offsetL = mul(offsetW, (float3x2) worldInverse);
texcoord += float2(mask, -mask) * offsetL;
Matcap反射效果
float2 CalcMatcapUV(FMaterialPixelParameters Parameters,float3 normalTexture)
{
float3 ViewVector=Parameters.CameraVector * -1;
float3 ViewSpaceRightVector=normalize(cross(ViewVector , mul( float3(0.0,1.0,0.0) , ResolvedView.ViewToTranslatedWorld )));
float3 ViewSpaceUpVector=cross(ViewVector ,ViewSpaceRightVector);
float3x3 Matrix=float3x3(
ViewSpaceRightVector.x,ViewSpaceUpVector.x,ViewVector.x,
ViewSpaceRightVector.y,ViewSpaceUpVector.y,ViewVector.y,
ViewSpaceRightVector.z,ViewSpaceUpVector.z,ViewVector.z
);
float3 ZeroOneNormal=mul(normalize(Parameters.WorldNormal+normalTexture),Matrix)*0.5+0.5;
return float2(ZeroOneNormal.x,ZeroOneNormal.y*-1);
}
这个不太行
//input normalTexture 法线贴图
float3 TangentWS=Parameters.TangentToWorld[0];
float3 NormalWS=Parameters.TangentToWorld[2];
float3 BinormalWS=cross(NormalWS, TangentWS);
float3 worldNormal;
worldNormal.x = dot(float3(TangentWS.x,BinormalWS.x,NormalWS.x), normalTexture);
worldNormal.y = dot(float3(TangentWS.y,BinormalWS.y,NormalWS.y), normalTexture);
worldNormal.z = dot(float3(TangentWS.z,BinormalWS.z,NormalWS.z), normalTexture);
worldNormal = normalize(worldNormal);
float3 e = normalize(GetWorldPosition(Parameters) - ResolvedView.WorldCameraOrigin);
float3 reflectVector = reflect(e, worldNormal);
float3 reflectVectorVS = normalize(mul(reflectVector,ResolvedView.TranslatedWorldToView));
float m = 2.82842712474619 * sqrt(reflectVectorVS.z + 1.0);
float2 cap = reflectVectorVS.xy / m + 0.5;
cap=cap*0.5 + 0.5;
return cap;
高光贴图位置调整
//input float2 LeftUp,LeftDown,RightUp,RightDown;
float3 viewW = mul(float3(0,0,1),(float3x3)View.ViewToTranslatedWorld);
float3 viewL = mul(viewW, (float3x2)GetPrimitiveData(Parameters.PrimitiveId).WorldToLocal);
float2 viewL2D = normalize(viewL.xy);
float2 Left=lerp(LeftUp,LeftDown,viewL2D.y);
float2 Right=lerp(RightUp,RightDown,viewL2D.y);
return lerp(Left,Right,viewL2D.x);
焦散
- dot(Parameters.WorldNormal,View.DirectionalLightDirection);
- Fresnel
乘以焦散Mask即可