Physics CG:菲涅尔(fresnel)反射

      这一篇是物理学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 100

        Pass
        {
            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 100

        Pass
        {
            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进行学习总结。

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
菲涅尔反射系数是描述光线从介质A射向介质B时,发生反射和折射的现象的物理量。在光的折射和反射中,菲涅尔反射系数用于描述光的入射波和反射波之间的关系。 Matlab是一种常用的科学计算和数据可视化工具,可以用来计算菲涅尔反射系数。我们可以利用Matlab中的物理光学库或者自定义函数来计算菲涅尔反射系数。 首先,我们需要确定介质A和介质B的折射率nA和nB。然后根据入射角度和折射角度的关系,可以使用Fresnel公式计算出垂直入射光的反射系数。 具体计算步骤如下: 1. 在Matlab中定义介质A和介质B的折射率nA和nB。 2. 输入入射角度θ,可以使用matlab函数sind和cosd将角度转换为弧度。 3. 根据Fresnel公式计算反射系数: - 入射波的振幅反射系数r_perpendicular = (nA * cos(θ) - nB * sqrt(1 - (nA/nB)^2 * sind(θ)^2)) / (nA * cos(θ) + nB * sqrt(1 - (nA/nB)^2 * sind(θ)^2)) - 入射波的振幅透射系数t_perpendicular = (2 * nA * cos(θ)) / (nA * cos(θ) + nB * sqrt(1 - (nA/nB)^2 * sind(θ)^2)) 4. 计算垂直入射光的反射系数R = r_perpendicular^2 和透射系数T = t_perpendicular^2。 通过以上步骤,我们可以得到菲涅尔反射系数R和透射系数T。在Matlab中,我们可以输入不同的折射率和入射角度,计算不同介质间的反射系数。 此外,我们还可以使用Matlab绘制出折射率和反射系数之间的关系图,以更好地理解光在不同介质中的传播过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值