The effect of the gravitational lens

                                     The effect of the gravitational lens

The image of the black hole and, accordingly, the effect of gravitational lensing caused by it. My first thought was to find a available code and customize for themselves, however, since no sufficiently good solutions and have not found (which is very strange, knowing how popular games on the space theme),  we decided to realize the effect of their own, and at the same time and share the result.

To begin, write a script that we hang on to the camera, and which will apply the shader to the on-screen image.

using UnityEngine;

public class Lens: MonoBehaviour {
    public Shader  shader;
    public float   ratio = 1;  	// The ratio of the height to the length of the screen to display properly shader
    public float   radius = 0; 	// The radius of the black hole measured in the same units as the other objects in the scene

    public GameObject BH;  // The object whose position is taken as the position of the black hole
    private Material _material; // Material which is located shader
    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) {
            // Find the position of the black hole in screen coordinates
            Vector2 pos = new Vector2(
       (BH.transform.position).x /,
       (BH.transform.position).y /;

            // Install all the required parameters for the shader
            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));
            // And is applied to the resulting image.
            Graphics.Blit(source, destination, material);


Now, on to the more important part: the writing of the shader.
First of all, we need to get the radius, depending on which will distort the image:

float2 offset = i.uv - _Position; // We shift our pixel to the desired position
float2 ratio = {_Ratio,1}; // determines the aspect ratio
float rad = length(offset / ratio); // define the distance

In physics, the formula of refraction of the light beam passing at a distance r from an object of mass M is given by:

For us M - mass of the black hole. Knowing the radius of the black hole is defined as

We obtain the following structure

float deformation = 2*_Rad*1/pow(rad*z,2); 

Where deformation - force distortion in each point, with the z - some dependence on the size of the distortion of distance at which the camera is located. To understand how this relationship is expressed, consider the formula Einstein ring.

In this formula, we are interested in its dependence on the distance, because most of it can be discarded only after watching

Since the shader handles 2-dimensional image, we can not tell how far away the object is. And although it can be implemented using a depth map, distort them to not turn out correctly, as required of all images that lie behind each of the objects. Therefore we assume that DL << DS and DL << DLS. Then we see that the size of the distortion is inversely proportional to the root of distances, we obtain...

deformation = 2*_Rad*1/pow(rad*pow(_Distance,0.5),2);

We now apply our deformation:

offset =offset*(1-deformation);

Return image back to the place and display.

offset += _Position;

half4 res = tex2D(_MainTex, offset);
return res;

Full shader code
Shader "Gravitation Lensing Shader" {
Properties {
    _MainTex ("Base (RGB)", 2D) = "white" {}

SubShader {
    Pass {
        ZTest Always Cull Off ZWrite Off
        Fog { Mode off }
        #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; // We shift our pixel to the desired position
            float2 ratio = {_Ratio,1}; // determines the aspect ratio
            float rad = length(offset / ratio); // the distance from the conventional "center" of the screen.
            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;} // verification of compliance with the Einstein radius
            //if (rad*_Distance<_Rad){res.r=0;res.g=0;res.b=0;} // check radius BH
            return res;


Fallback off


What it looks like in the editor, you can view a short video.

All data posted on the site represents accessible information that can be browsed and downloaded for free from the web.



User replies

No replies yet