几何向量:计算光线折射refract向量

紧接上一篇:http://blog.csdn.net/yinhun2012/article/details/79466517

        其实光线除了反射现象外,还有折射现象,打个比方,你看水塘中鱼儿游动,表面上看起来鱼儿很贴近水面,同时也感觉水底很浅,结果一脚捅下去,直接没到大腿了,这就是光的折射产生的一种视觉欺骗。

        光线折射的产生,其实是因为光线通过不同介质所产生的“偏折”现象。

        ps:光线的“偏折”现象实际上和光的粒子性有关,这里我随便聊一下,光的波粒二象性说明光子也是粒子,光子的吸收与发出遵守粒子的几率布,光的运动路线,也就是光子吸收、发出的最大几率的地方,当然物理学中也认可光是几率波,由于光的运动路线是光子被吸收,发出的最大几率的地方,那么光线的方向,就会向着吸收、发出光的可能性多的前进,也就是那个方向吸收这种光子的可能性大,就向这个方向偏折,我们假设光子在真空中,单位时间内被吸收、发出了N次,而在介质中,在相同的时间内被吸收、发出了M次,光子本身的速度不变,由于被吸收发出,运动路程减少,速度减小,那么,则,当光线以角入射到界面上的时候,由于光子的法线方向吸收光子、发出光子的可能性大,光线应该向着法线方向偏折。回到正题,这里我们是来学习图形学的,我们暂时只来讨论光的折射:光从一种介质斜射入另一种介质时,传播方向发生改变,从而使光线在不同介质的交界处发生偏折

        这里来绘画一幅图看一下上面水塘的现象,如下图:

眼睛欺骗了我们,让我们把虚拟鱼的坐标当成了真实鱼的坐标。

计算机图形学中,我们利用斯涅尔定律表示折射:

斯涅尔定律是描述光或其他的波,从一个介质进入另一介质,入射角与折射角关系的一个公式,此定律得到入射角的正弦值与折射角的正弦值的比值为一定值,而此一定值跟入射与折射介质有关。

公式也简单:n1 * sinθ1 = n2 * sinθ2,

其中n1,n2表示两种介质的折射率。

如下图:


ps:一般情况下,我们认为真空中(或者说空气中)折射率n = 1,毕竟我们起码要选取一个标准阀值,来推导其他介质的折射率,这里直接规定真空(空气)为标准1,那么其他介质的折射率都比真空要大,也就是>1。

那么接下来我们通过入射光线计算折射光线,如下图:


我们通过建立单位圆辅助计算,通过标量推导出向量的模长,然后通过向量平行和同异方向推导出向量,推出OB = OD + DB = -|cosβ|*OE + |sinβ/sinθ|*(AO + |cosθ|* OE),推导到这里,我们就把OB的公式用cos sin以及入射光向量AO和介质法向量N来计算了,这时候我们就思考如何计算cosβ cosθ,让这两个值使用已知的n1,n2,AO,OE来替换,如下图:


我们通过斯涅尔定律:n1 * sinθ = n2 * sinβ得到sinβ = n1/n2 * sinθ

点积的概念推出cosθ

cosβ的推导可以先试用sinβ替换,然后替换斯涅尔定律中计算的sinβ得到cosθ的表示,

这个时候我们所有的未知量就全部通过已知值(n1,n2,AO,OE)来表示了,如下图:


算出来的公式咋一看十分复杂,接下来我们就要通过程序来进行模拟验证,如下图:


验证程序下载地址:https://download.csdn.net/download/yinhun2012/10275193




  • 15
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 实时折射 Shader 是在渲染透明物体时,通过计算物体内部的折射光线,使得物体表面呈现出折射的效果。以下是一个简单的 Unity 实时折射 Shader 示例: ```shader Shader "Custom/Refraction" { Properties { _MainTex ("Texture", 2D) = "white" {} _Refraction ("Refraction", Range(0.0, 1.0)) = 1.0 _BumpMap ("Normalmap", 2D) = "bump" {} _RefractionColor ("Refraction Color", Color) = (1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float4 tangent : TANGENT; float2 texcoord : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float3 worldNormal : TEXCOORD1; float3 worldPos : TEXCOORD2; float3 worldRefract : TEXCOORD3; float3 worldView : TEXCOORD4; float3 worldIncident : TEXCOORD5; float3 worldTangent : TEXCOORD6; float3 worldBinormal : TEXCOORD7; UNITY_VERTEX_OUTPUT_STEREO }; sampler2D _MainTex; sampler2D _BumpMap; float4 _MainTex_ST; float _Refraction; float3 _WorldSpaceCameraPos; float4x4 unity_WorldToObject; float4x4 unity_ObjectToWorld; float4x4 unity_CameraToWorld; float4x4 unity_WorldToCamera; float3 TransformWorldToTangent(float3 worldVec, float3 worldNormal, float3 worldTangent, float3 worldBinormal) { float3 tangentVec = float3(dot(worldVec, worldTangent), dot(worldVec, worldBinormal), dot(worldVec, worldNormal)); return tangentVec; } void vert (inout appdata v) { UNITY_INITIALIZE_OUTPUT(appdata, v); float3 worldNormal = UnityObjectToWorldNormal(v.normal); float3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz); float3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; float3 worldPos = UnityObjectToWorld(v.vertex); float3 worldView = Normalize(_WorldSpaceCameraPos - worldPos); float3 worldIncident = TransformWorldToTangent(-worldView, worldNormal, worldTangent, worldBinormal); v.worldNormal = worldNormal; v.worldPos = worldPos; v.worldIncident = worldIncident; v.worldView = worldView; v.worldTangent = worldTangent; v.worldBinormal = worldBinormal; v.uv = TRANSFORM_TEX(v.texcoord, _MainTex); UNITY_TRANSFER_FOG(v, float4(v.vertex.xyz, 1)); } float3 Refract(float3 I, float3 N, float eta) { float cosi = dot(I, N); float cost2 = 1.0 - eta * eta * (1.0 - cosi * cosi); return cost2 > 0.0 ? eta * I - (eta * cosi + sqrt(cost2)) * N : float3(0, 0, 0); } float4 frag (v2f i) : SV_Target { float3 normal = UnpackNormal(tex2D(_BumpMap, i.uv)); float3 worldNormal = normalize(i.worldNormal); float3 worldTangent = normalize(i.worldTangent); float3 worldBinormal = normalize(i.worldBinormal); float3 worldIncident = normalize(i.worldIncident); float3 worldRefract = Refract(worldIncident, worldNormal, 1.0 / _Refraction); float2 refractOffset = i.uv + worldRefract.xy * (_Refraction * 0.01); float4 refractColor = tex2D(_MainTex, refractOffset); float3 viewDir = normalize(i.worldView); float fresnel = pow(1.0 - dot(-viewDir, worldNormal), 5.0); float3 refractTint = lerp(float3(1, 1, 1), _RefractionColor.rgb, _RefractionColor.a); float3 color = refractColor.rgb * refractTint; return float4(color * fresnel, refractColor.a); } ENDCG } } FallBack "Diffuse" } ``` 上述代码中,我们首先定义了一些属性,包括主纹理、折射系数、法线贴图、折射颜色等。然后我们定义了两个结构体,一个用于传递顶点数据,一个用于传递片元数据。在顶点着色器中,我们计算了物体表面的法向量、切线、副切线、视线和入射光线等信息,并将其保存在传递顶点数据的结构体中。在片元着色器中,我们首先使用法线贴图计算物体表面的法向量,然后根据折射系数计算物体内部的折射光线,并将其应用到主纹理上,最后计算菲涅尔反射系数,并将其与折射颜色混合,得到最终的颜色。 注意:上述代码仅为示例代码,实际使用时还需要根据场景和需求进行相应的调整和优化。 ### 回答2: Unity 实时折射shader是一种用于实时渲染中模拟物体折射效果的技术。通过这种shader,可以使物体在光线穿过或经过物体时发生折射,从而产生逼真的光学效果。 实时折射shader的实现原理是基于折射率。每个物体都有一个折射率,折射率决定了光线在物体中传播时的速度和角度的改变。shader根据光线从场景中的其他物体射出时发生的折射,来计算物体表面上的折射效果。这个计算可以在真实时间内进行,因此称之为实时折射。 实时折射shader包括两个主要部分:反射和折射。反射是物体表面产生镜面反射的效果,折射光线穿过物体并改变方向的效果。在shader代码中,通过计算光线的入射方向和折射方向之间的角度差,来确定反射和折射的强度和方向。使用非常接近真实光学效果的折射模型,可以使物体看起来更加真实。 在Unity中实现实时折射shader,我们通常使用一些现成的shader库和工具,例如Amplify Shader Editor等。这些工具提供了图形界面和预设功能,使得创建和调整折射shader变得更加容易。我们可以通过调整折射率和材质的属性,改变折射效果的强度和形态。同时,还可以结合其他效果,如反射、环境光遮蔽等,来进一步增强折射效果的真实感。 总之,Unity实时折射shader是一种模拟物体折射效果的技术,通过计算光线折射和反射来产生真实的光学效果。通过使用现成的shader库和工具,我们可以在Unity中轻松实现和调整这种效果,使得物体的渲染更加逼真和吸引人。 ### 回答3: Unity 实时折射着色器是一种在Unity引擎中使用的效果,主要用于在游戏或应用程序中实现物体对光线折射效果。折射效果是指当光线从一个介质进入另一个介质时,光线的传播方向发生改变的现象。 在Unity中实现实时折射效果需要使用着色器语言编写自定义的着色器。着色器是一种控制对象在屏幕上渲染的方式的程序,它可以对光照、材质和纹理进行处理。 实时折射着色器的实现通常分为以下几个步骤: 1. 计算入射光线:首先需要计算从光源到物体表面的光线矢量,这可以通过在着色器中使用光照模型和材质属性来完成。 2. 计算折射光线:根据物体表面的法线和折射率,可以使用数学公式计算光线从一个介质到另一个介质的折射光线矢量折射光线计算需要考虑入射角和材质的折射率。 3. 折射纹理采样:为了模拟物体的折射效果,通常需要将折射纹理与折射光线相结合。通过在着色器中采样和使用折射纹理,可以在物体内部产生扭曲和变形的视觉效果。 4. 渲染:最后,使用计算得到的折射光线和纹理数据,将物体渲染到屏幕上。折射光线的方向和强度会影响物体的透明度和光线的散射效果。 通过使用Unity实时折射着色器,可以在游戏或应用程序中实现更加真实的光线和材质效果。这可以用来创建逼真的水面、玻璃、水晶等物体,并为用户提供更加沉浸式的视觉体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值