Blend Cubemaps in reflection probe

I am trying to make a reflection probe render once every second, and blend between the old render and the new one. See how it looks like in Zelda BOTW:
https://twitter.com/brainchildlight/status/937439281890467840

.

I am currently only able to render once every second, but not blend between the old and new cubemap. It instantly swaps to the new one. I found the ReflectionProbe.BlendCubemap function but have had no success with it. Most attempts I make crash Unity, and since there are a grand total of zero examples of it being used online I have a hard time even figuring out if it is the way to go.

I am hoping not to have to rely on blending the cubemaps inside the shader since I want this to work on all objects with all kinds of materials, not just certain ones which are blend-approved…

Here’s some of the code I’ve tried, which in my mind should probably work. All it does is render a new reflection every second with no blending. But as I said there isn’t really that much information available about this stuff so I have a hard time to figure out what I am doing wrong.

public class PlayerReflectionProbe : MonoBehaviour
    {
        private float timer;
        public float updateTime = 1f;
        private ReflectionProbe thisProbe;
        public Texture oldCubemap;
        public Texture newCubemap;
        private RenderTexture finalCubemap;
        public float blend = 0.5f;
        public RenderTexture render;

        void Awake()
        {
            // Set up reflection probe component.
            if (thisProbe == null)
            {
                thisProbe = GetComponent<ReflectionProbe>();
            }
            // Set up final texture diplayed by render probe.
            render = new RenderTexture(128, 128, 0);
            render.dimension = UnityEngine.Rendering.TextureDimension.Cube;
            render.useMipMap = true;
            thisProbe.customBakedTexture = render;
        }

        // Update is called once per frame
        void Update()
        {
            // Blend between old and new.
            ReflectionProbe.BlendCubemap(oldCubemap, newCubemap, blend, render);

            // Assign final render to probe ???
            thisProbe.customBakedTexture = render;

            // Update blend
            blend -= Time.deltaTime;
            if (blend <= 0f)
                blend = 1f;

            // Render new once every second, and save old render.
            timer += Time.deltaTime;
            if (timer >= updateTime)
            {
                oldCubemap = thisProbe.texture;

                thisProbe.RenderProbe();
                newCubemap = thisProbe.texture;

                timer = 0f;
            }
        }
    }

Any help at all is greatly appreciated

Here is some code I made a while ago that I forgot about that basically does what you asked, and also has a bunch of features. I forget how exactly you use it, but I tested it and it works fine for me. When set to medium, it updates the cubemap every n seconds, and interpolates to it smoothly. At high, it just updates every frame. At low, I think it just uses the skybox.

public class ReflectionProbeBlender : MonoBehaviour {

    public ReflectionProbe outputProbe;
    public ReflectionProbe samplerProbe;
    public ReflectionProbe previousProbe;
    public LayerMask cullingMask;
    public ReflectionRes lowReflectionResolution;
    public ReflectionRes mediumReflectionResolution;
    public ReflectionRes highReflectionResolution;
    private ReflectionRes currentReflectionRes;
    public float updateReflectionEveryNSeconds = 1f;
    public WaterReflectionQuality reflectionQuality;
    private float updateTimer;
    private RenderTexture blendedTexture;
    public enum ReflectionRes
    {
        _16,
        _32,
        _64,
        _128,
        _256,
        _512,
        _1024,
        _2048
    };
    public enum WaterReflectionQuality
    {
        Low,
        Medium,
        High
    };
    private int resolution;
    private int GetReflectionRes(ReflectionRes res)
    {
        switch (res)
        {
            case ReflectionRes._16:
                return 16;
            case ReflectionRes._32:
                return 32;
            case ReflectionRes._64:
                return 64;
            case ReflectionRes._128:
                return 128;
            case ReflectionRes._256:
                return 256;
            case ReflectionRes._512:
                return 512;
            case ReflectionRes._1024:
                return 1024;
            case ReflectionRes._2048:
                return 2048;
            default:
                return 16;
        }
    }

    private void Start()
    {
        
        if (!UnityEngine.QualitySettings.realtimeReflectionProbes)
        {
            reflectionQuality = WaterReflectionQuality.Low;
        }
        samplerProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
        previousProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
        switch (reflectionQuality)
        {
            case WaterReflectionQuality.Low:
                currentReflectionRes = lowReflectionResolution;   
                samplerProbe.cullingMask = 0;
                previousProbe.cullingMask = 0;
                outputProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Custom;
                samplerProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
                previousProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
                samplerProbe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.OnAwake;
                previousProbe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.OnAwake;
                outputProbe.customBakedTexture = samplerProbe.texture;
                break;
            case WaterReflectionQuality.Medium:
                currentReflectionRes = mediumReflectionResolution;
                samplerProbe.cullingMask = cullingMask;
                previousProbe.cullingMask = cullingMask;
                outputProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Custom;
                samplerProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
                previousProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
                samplerProbe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.ViaScripting;
                previousProbe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.ViaScripting;
                break;
            case WaterReflectionQuality.High:
                currentReflectionRes = highReflectionResolution;
                samplerProbe.enabled = false;
                previousProbe.enabled = false;
                outputProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Realtime;
                outputProbe.refreshMode = UnityEngine.Rendering.ReflectionProbeRefreshMode.EveryFrame;
                break;
        }
        resolution = GetReflectionRes(currentReflectionRes);
        blendedTexture = new RenderTexture(resolution, resolution, 0, RenderTextureFormat.ARGBHalf);
        blendedTexture.isCubemap = true;
        blendedTexture.useMipMap = true;

        outputProbe.resolution = resolution;
        samplerProbe.resolution = resolution;
        previousProbe.resolution = resolution;
    }

    private void LateUpdate()
    {
        if(reflectionQuality == WaterReflectionQuality.Medium)
        {
            updateReflections();
        }
    }
    private void updateReflections()
    {
        updateTimer += Time.deltaTime;
        if (updateTimer >= updateReflectionEveryNSeconds)
        {
            updateTimer -= updateReflectionEveryNSeconds;
            //samplerProbe.RenderProbe();
            ReflectionProbe temp = previousProbe;
            previousProbe = samplerProbe;
            samplerProbe = temp;
            samplerProbe.RenderProbe();
        }
        //Debug.Log
        ReflectionProbe.BlendCubemap(previousProbe.texture, samplerProbe.texture, updateTimer / updateReflectionEveryNSeconds, blendedTexture);
        outputProbe.customBakedTexture = blendedTexture;
    }
}