Unityでの現実的な重力レンズ

画像
銀河団RCS2 032727-132623によって引き起こされる重力レンズの効果

最近、Unityにブラックホールのかなりもっともらしいイメージを実装する必要があり、それに応じて重力レンズ効果がそれによって引き起こされました。 最初の考えは、既製の実装を見つけてそれを自分で微調整することでしたが、単一の十分なソリューション(非常に奇妙で、宇宙ゲームの人気を知っている)が見つからなかったため、私は効果を自分で実装し、同時に結果をhabroコミュニティと共有することにしました。

まず、カメラに掛けて、画面に表示される画像にシェーダーを適用するスクリプトを作成します。

スクリプト
using UnityEngine; [ExecuteInEditMode] public class Lens: MonoBehaviour { public Shader shader; public float ratio = 1; //    ,     public float radius = 0; //       ,       public GameObject BH; //,        private Material _material; //      protected Material material { get { if (_material == null) { _material = new Material (shader); _material.hideFlags = HideFlags.HideAndDontSave; } return _material; } } protected virtual void OnDisable() { if( _material ) { DestroyImmediate( _material ); } } void OnRenderImage (RenderTexture source, RenderTexture destination) { if (shader && material) { //       Vector2 pos = new Vector2( this.camera.WorldToScreenPoint (BH.transform.position).x / this.camera.pixelWidth, 1-this.camera.WorldToScreenPoint (BH.transform.position).y / this.camera.pixelHeight); //      material.SetVector("_Position", new Vector2(pos.x, pos.y)); material.SetFloat("_Ratio", ratio); material.SetFloat("_Rad", radius); material.SetFloat("_Distance", Vector3.Distance(BH.transform.position, this.transform.position)); //    . Graphics.Blit(source, destination, material); } } } 

次に重要な部分は、シェーダー自体を作成することです。

まず、画像を歪ませる半径に応じて、半径を取得する必要があります。
 float2 offset = i.uv - _Position; //      float2 ratio = {_Ratio,1}; //    float rad = length(offset / ratio); //  

物理学では、質量Mのオブジェクトから距離rを通過する光線の屈折の式は次の形式になります。
画像
私たちにとって、Mはブラックホールの質量です。 ブラックホールの半径は
画像
次の構造を取得します
 float deformation = 2*_Rad*1/pow(rad*z,2); 

ここで、変形は特定の各ポイントでの歪みの力であり、zはカメラが配置されている距離に対する歪みの大きさの特定の依存性です。 この依存関係がどのように表現されるかを理解するために、アインシュタインリングの公式に目を向けます。
画像
どこで
画像
この式では、距離に依存することに興味があります。なぜなら、そのほとんどは、
画像
シェーダーは2次元の画像を処理するため、オブジェクトの距離はわかりません。 また、これは深度マップを使用して実装できますが、各オブジェクトの背後にあるすべての画像が必要になるため、正しく歪めることはできません。 したがって、D L << D SおよびD L << D LSと仮定します。 次に、歪みのサイズが距離のルートに反比例することがわかります。
 deformation = 2*_Rad*1/pow(rad*pow(_Distance,0.5),2); 

次に、変形を適用します。
 offset =offset*(1-deformation); 

画像を元の場所に戻し、表示します。
 offset += _Position; half4 res = tex2D(_MainTex, offset); return res; 

完全なシェーダーコード
 Shader "Gravitation Lensing Shader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Pass { ZTest Always Cull Off ZWrite Off Fog { Mode off } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest #include "UnityCG.cginc" uniform sampler2D _MainTex; uniform float2 _Position; uniform float _Rad; uniform float _Ratio; uniform float _Distance; struct v2f { float4 pos : POSITION; float2 uv : TEXCOORD0; }; v2f vert( appdata_img v ) { v2f o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord; return o; } float4 frag (v2f i) : COLOR { float2 offset = i.uv - _Position; //      float2 ratio = {_Ratio,1}; //    float rad = length(offset / ratio); //    "" . float deformation = 1/pow(rad*pow(_Distance,0.5),2)*_Rad*2; offset =offset*(1-deformation); offset += _Position; half4 res = tex2D(_MainTex, offset); //if (rad*_Distance<pow(2*_Rad/_Distance,0.5)*_Distance) {res.g+=0.2;} //     //if (rad*_Distance<_Rad){res.r=0;res.g=0;res.b=0;} //   return res; } ENDCG } } Fallback off } 

以上です! あなたは結果を楽しむことができます:


このシェーダーは、1つの巨大なオブジェクトにのみ歪みを実装します。 ブラックホールの前にあるものを表示するために、メインカメラの上に描画する別のカメラを使用しました。 このソリューションはエレガントとは言えませんが、私の場合はうまく機能します。

PSポストエフェクトは、UnityのProバージョンでのみ機能することに注意してください。

Source: https://habr.com/ru/post/J235729/


All Articles