这一篇是物理学CG的开篇,其实我多开了一个分类也是心理无奈的,自身水平有限,而很多物理图形学知识积累太少,有些甚至根本没学过。然而高级图形学知识中,或者说高级图形着色效果中,很大一部分是基于“真实”物理公式来实现的。但是,我看了部分物理公式和推导过程(我甚至在家里翻箱倒柜找到一本物理书看了一些),这些物理公式给我的感觉就是公式优雅,推导过程完备,匹配真实世界物理规律,但是就是不知道这些公式怎么来的。
记得以前的物理老师跟我们说的是,物理公式是前人实验总结的数学表达结果,我们也亲手做过很多简单的物理实验,理解其数学表达式的含义。直到质能方程、麦克斯韦方程、高斯定理等,我的天,我最多只能用公式去计算。其完整的推导过程以及过程后面的含义都让我迷迷糊糊,最后接受的教条就是能用就行,大致理解其中的道理。因为我也不是数学系的,线性代数/微积分及以下数学领域常用的公式推导还能勉强讲解,至于高端一点的物理公式我也只能结合其数学表达式使用和找一些文献查看其由来或者推导了,至于让我深层解释其意义,我是无能为力。
当然学到今天这个地步,不涉及到物理图形学,基本上就没有更深的东西可以来学习了,其他常用图形学技术,已经可以默默的看一个懂一个,懂一个写一个了,所以还是要开展一些物理相关图形学的学习。
好了,说到这里,今天主要来说一下菲涅尔公式,先回顾一下以前的菲涅尔折射计算:refract计算 菲涅尔反射
这里主要来说一下菲涅尔反射(折射我们都知道,光线通过不同介质的时候会发生折射,折射公式n1 * sinθ1 = n2 * sinθ2,n1θ1为入射折射率和入射角,n2sinθ2为反射折射率和折射角),原文描叙如下:
当你站在湖边,低头看脚下的水,你会发现水是透明的,反射不是特别强烈,如果你抬头看远处的湖面,会发现水不是透明的,反射非常强烈,这就是“菲涅尔效应”。从数学上来讲,就是视线垂直于水平面呈90°,则反射较弱,而视线方向逐渐改变,非垂直于水平面,而是与水平面夹角从90°慢慢减小,反射就越明显。如果你正对着看一个球体,视线聚焦于球心的时候反射较弱,视线越靠近球体边缘反射越强,这个菲涅尔反射效应在现实世界和三维图形中有很广泛的使用。
这是我查到的菲涅尔反射应用简化计算公式:
float fresnel = F_base + F_scale*((1-v*n)^power); F_base/F_scale/power都是菲涅尔控制系数,可以抛出调参的。
顺便解释一下,v为视点到视口的单位朝向向量,n为视点的单位法向量。所以综合公式来看,当“低头看向脚下湖水”,dot(v,n)趋于1,fresnel趋于F_base,当“抬头看向远处的湖面”,dot(v,n)趋于0,fresnel趋于F_base + F_scale(power幂次方为大于1的情况)。那么总结来说,这个公式能达到越抬头望向远处,fresnel值越大(F_base/F_scale均为正数)。
这也很符合“菲涅尔效应”,如果将fresnel作为颜色混合权重参数,混合因子为原始颜色和反射颜色,那么“低头”时fresnel越小,则越表现原始颜色,”抬头“时fresnel越大越表现反射颜色。
如果看了光照模型那些应该就感觉的出来,和光照模型中计算diffuse和specular很像。
既然知道这个菲涅尔反射意义和应用计算公式,那么接下来我们就来实现一下:
Shader "Unlit/FresnelReflectShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}_FresnelBase("F Base",Range(0,1)) = 1_FresnelScale("F Scale",Range(0,1)) = 1_FresnelPower("F Power",Range(1,10)) = 5_FresnelColor("F Color",Color) = (1,1,1,1)}SubShader{//前向渲染模式Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL; /*建模空间法向量源数据*/};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float3 worldV : TEXCOORD1; /*世界空间视点到视口朝向向量*/float3 worldN : TEXCROOD2; /*世界空间视点法向量*/};sampler2D _MainTex;float4 _MainTex_ST;/*三个菲涅尔控制系数*/float _FresnelBase; float _FresnelScale;float _FresnelPower;/*菲涅尔反射颜色*/float4 _FresnelColor;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);//处理世界空间中视点(顶点)到视口朝向向量o.worldV = normalize(WorldSpaceViewDir(v.vertex));//o.worldV = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex)).xyz;//处理建模到世界空间的法向量变换o.worldN = normalize(UnityObjectToWorldNormal(v.normal));return o;}/*颜色混合*/fixed4 blendColor(fixed4 col, fixed4 fcol, float wei){return col * (1-wei) + fcol * wei;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);//逐片段计算fresnel值float fresnel = saturate(_FresnelBase + _FresnelScale * pow(1 - dot(i.worldN, i.worldV), _FresnelPower));//混合源颜色和反射颜色权重fixed4 bcol = blendColor(col, _FresnelColor, fresnel);return bcol;}ENDCG}}
}
使用到了unityCG.cginc自带的辅助函数,如下:
表现效果如下:
比较符合在湖边观察湖水的情况,同时上面的sphere也展示了fresnel效应在普通物体上的表现效果。
当然了,我们可以修改一下shader增加环境反射,将fresnelColor修改为采样环境反射cubemap的颜色值,实现表现效果比较好的fresnel效应,如下:
Shader "Unlit/FresnelReflectShader"
{Properties{_MainTex ("Texture", 2D) = "white" {}_FresnelBase("F Base",Range(0,1)) = 1_FresnelScale("F Scale",Range(0,1)) = 1_FresnelPower("F Power",Range(1,10)) = 5_FresnelCube("F Cube",CUBE) = "" {}}SubShader{//前向渲染模式Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase" }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;float3 normal : NORMAL; /*建模空间法向量源数据*/};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float3 worldV : TEXCOORD1; /*世界空间视点到视口朝向向量*/float3 worldN : TEXCROOD2; /*世界空间视点法向量*/float3 worldR : TEXCOORD3; /*环境反射向量*/};sampler2D _MainTex;float4 _MainTex_ST;/*三个菲涅尔控制系数*/float _FresnelBase; float _FresnelScale;float _FresnelPower;/*环境盒子贴图*/samplerCUBE _FresnelCube;v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);//处理世界空间中视点(顶点)到视口朝向向量o.worldV = normalize(WorldSpaceViewDir(v.vertex));//o.worldV = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex)).xyz;//处理建模到世界空间的法向量变换o.worldN = normalize(UnityObjectToWorldNormal(v.normal));//使用cg自带reflect采样o.worldR = reflect(-o.worldV, o.worldN);return o;}/*颜色混合*/fixed4 blendColor(fixed4 col, fixed4 fcol, float wei){return col * (1-wei) + fcol * wei;}fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);fixed4 rcol = texCUBE(_FresnelCube, i.worldR);//逐片段计算fresnel值float fresnel = saturate(_FresnelBase + _FresnelScale * pow(1 - dot(i.worldN, i.worldV), _FresnelPower));//混合源颜色和反射颜色权重fixed4 bcol = blendColor(col, rcol, fresnel);return bcol;}ENDCG}}
}
效果图如下:
这也是三维图形学中对菲涅尔效应的应用。
后面持续对基于物理学的shader进行学习总结。