Seamless 2D wrap-around effect

Seamless 2D wrap-around effect

I am trying to show multiple images on a grid system which are randomly generated and the grid is moving from right to left.
What my client wants is when a part of a picture goes off the left edge of the viewport, it should start to peek in from the right, similar to the image shown below:

You can see the red and blue images each represent the same object. When they go off the left edge of the screen, they appear the right edge. It should happen seamlessly, without a sudden "pop" appearing out of nowhere.
I think it can be done via a shader, but I'm not a shader programmer. How can I achieve this effect?


Answer 1:

You can do this with magic (shader)!

Secret: Cutting Assistant In Half

Of course, we know she is never actually cut in half. So, there has to be some logical explanation for it all. Well, he certainly relies on the help of an assistant alright. But it’s not one – he has two of them!

as you see in the below picture we need two persons or objects to achieve this effect.

enter image description here

you can achieve this effect easily by using world space shader that I explained before
I did this with childing twin but you can use code to teleport.

enter image description here
enter image description here

enter image description here

Shader "Smkgames/worldSpaceFade" {
    Properties {
         _Size("Size",Vector) = (2,0,2,0)
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
         _BumpMap ("Bumpmap", 2D) = "bump" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    SubShader {
 Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
     Pass {
        ZWrite On
        ColorMask 0
        LOD 200

        #pragma surface surf Standard fullforwardshadows alpha:fade

        #pragma target 3.0

        sampler2D _MainTex,_BumpMap;

        struct Input {
            float2 uv_MainTex;
            float2 uv_BumpMap;
            float3 worldPos: TEXCOORD2;

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float4 _Size;

        void vert (inout appdata_full v, out Input o){
            o.worldPos = mul(unity_ObjectToWorld, v.vertex);


        void surf (Input IN, inout SurfaceOutputStandard o) {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            float4 equation = (IN.worldPos.x/_Size.x)*(IN.worldPos.x/_Size.x);
            float4 InvertEquation = (IN.worldPos.z/_Size.z)*(IN.worldPos.z/_Size.z);
            float4 finalEquiation = max(equation,InvertEquation);
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = step(finalEquiation,0.5);
             o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    FallBack "Diffuse"

Object Pooling Concept

another concept that you should know is Object Pooling.
when one person or object arrived to the destination then move it back to the first position and her twin will appears.

enter image description here

Dynamic Teleport

better way is Teleporting by code:

enter image description here

using UnityEngine;

public class Teleport : MonoBehaviour
public Transform teleportTarget;
private void OnTriggerEnter(Collider other) {
    var pos = other.transform.position;
    pos.x = teleportTarget.position.x;
    other.transform.position = pos;

Answer 2:

Myself, I wouldn’t do this with a shader, but with a second camera.

The idea is we’ll take our original/main/central camera, and make a copy that butts-up perfectly against its left side. As an object exits the left side of our main camera’s view, it will enter the right side of this camera’s view. If we draw both cameras to the same output buffer (the player’s screen/window), then we’ll see two images of the object: one exiting to the left, and one entering from the right.

All it takes is a little coordination to make sure the alignment is perfect.

To keep things simple, I’m going to make some assumptions:

  • Your central camera is…
    • orthographic
    • located at world x = 0
    • in default orientation (looking along the z+ axis, with y+ up)
    • unchanging over time (you’re not modifying its location or parameters)
  • Your game window doesn’t change size / mobile orientation during play
  • Your display objects move from right to left only, never left to right

(If you need to break these rules, there are solutions available, they’re just more complicated than the simple version I’ll show here)

To start, create a copy of your central camera, call it “wraparound”

  • Make it a child of the central camera (for ease of positioning)
  • Remove its AudioListener (so all audio is heard by just one camera, and we don’t get errors)
  • Set its Depth to 0 (instead of the default -1, so it renders after the central cam)
  • Set is Clear Flags to “Don’t Clear” (so it keeps the colour & depth drawn by the central cam)

Attach to it a script something like this:

public class WraparoundCamera : MonoBehaviour {
    // Quick & dirty singleton.
    public static WraparoundCamera current;

    Camera central;
    float halfViewWidth;

    void Awake() {
        // Make ourselves easy for other scripts to find.
        WraparoundCamera.current = this;

        // Position this camera to butt up to the central camera's left edge.
        central = transform.parent.GetComponent<Camera>();
        halfViewWidth = central.aspect * central.orthographicSize;
        transform.localPosition = Vector3.left * halfViewWidth * 2f;

    // Objects will call this to wrap themselves around to the other side of the view.
    public bool WrapMeIfNeeded(Renderer renderer) {
        // If the object is still in range to be seen by the central camera, don't move it.
        if(renderer.bounds.max.x >= -halfViewWidth)
            return false;

        // Once it's passed the left edge of the central camera,
        // teleport it over on full view width, so it's at the right edge.
        renderer.transform.position += Vector3.right * halfViewWidth * 2f;
        return true;

Then your moving objects can use a script a bit like this:

void Update() {
    transform.position += velocity * Time.deltaTime;