LatLong RenderTexture SkyRenderer

Hi all,
I was trying to do a simple thing for one of our tools that generates and HDR. So what I wanted was to use our preview LatLong HDR RenderTexture as a SkyRenderer. I tried to do a simple Sky (using the HDRISky as a base) that just reads the RenderTexture and then with a shader (similar to the PanoramicSky) render it in the SkyRenderer. The problem is that I don’t see any result. Is there any way to trigger a Sky reevaluation this.
We were trying to go this route, to avoid saving as an exr, import as cubemap and loaded it in the HDRISky, since saving and importing takes quite some time and breaks a “bit” the flow of the tool.

Cheers,
Daniel

Hi @danielgsantana ,

Are you using HDRP?

What little I’ve dabbled with custom sky rendering, it seems to be working ok for me. If I’m assuming that you are using HDRP, did you add the Visual Environment override and actually activate your sky and did you also add the override for your custom sky? There’s Update Mode parameter, which allows you to define when it updates. I think the default is On Changed.

At least the things I’ve tried update instantly when that Update Mode is On Changed.

Hi @Olmi ,
Yes, I’m on HDRP. My problem is that the texture I want to use doesn’t exist physically on the disk, it’s a render texture, that I want the SkyRenderer to read outside of play mode, so we can show the result of our tool. I do have my custom Sky and SkyRenderer setup on the Visual Enviroment.

Here is an example of our tool, but running in standard pipeline, where we set the Sky material with a RenderTexture preview.

That didn’t need lots of fiddling to achieve, but in HDRP which has so many features and should handle something like this easily, I have found that is much more difficult due to scarce documentation.

Cheers,
Daniel

1 Like

Hm. I wonder what you are doing so I could help a bit more.
I just did a quick test, RenderTextures show up fine here (from disk) ?
So just to clarify, you can get a RenderTexture that is on disk to show up, but not one that you render somewhere else?

Yup, exactly, so I “fill” a RenderTexture on our tool, and then the on the SkyRenderer (RenderSky method) I get the RenderTexture (store in a static variable, which isn’t a very good option, I try to avoid static) and pass it to the shader using the CoreUtils.DrawFullScreen.
Just a side note, I can make all this work using textures from disk (exporting the RenderTexture every few seconds), but this causes lots of lags of course when you try to do what we show on the previous video.
Also, most of the SkyRenderers code use Cubemaps, which is something I have been unable to create on our side using the latlong RenderTexture we produce :(, there should be an easy way to convert the RenderTexture to a Cubemap, as easy as there is on import.

Ok, well I got the texture working from a compute shader too. It’s nowhere on the disk and updates every frame. Just like with compute shaders with the old renderpipeline.

Uhm interesting. Are you doing anything special with RenderTexture? And are you updating the texture on the RenderSky method? I haven’t tried to use compute shaders, just a regular shader that reads the latlong texture doing an xyz to uv translation on the vertex coordinates.

You have to assign the RenderTexture you have in your SkySettings, then also apply that to the material in your SkyRenderer implementation.

Well, I did that already, but for some reason, it wasn’t working. Going to review code, must be something I have missed.
Thanks

Create public RenderTextureParameter in the class you inherit from SkySettings, then in your RenderSky access that parameter’s value and use SetTexture to set it to the material property block you are using. Use the name you have defined in your sky renderer shader. And make sure you have the UVs correct etc. There’s many things that might make it look like it’s not rendering… I’m sure it will work if you’re on 7.1.5.

1 Like

Ok, I picked this again, but no success, I will put all the code I have, may someone can help a little bit. The result is just weird, and again trying to set the RenderTexture (generate at runtime) at runtime, just doesn’t work.
Code to set the RenderTexture at Runtime (snippet only):

            HDRCreatorSky sky = null;
            var found = false;
            var volumes = FindObjectsOfType<Volume>();
            foreach (var volume in volumes)
            {
                if (volume.sharedProfile.Has<HDRCreatorSky>())
                {
                    found = volume.sharedProfile.TryGet(out sky);
                    break;
                }
            }
            sky.skyRenderTexture.value = _cacheBackground;

Sky Settings

using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;

namespace HeadlessStudio.HDRCreator
{
    [VolumeComponentMenu("Sky/HDR Creator Sky")]
    // Use 99 for the sky id.
    [SkyUniqueID(99)]
    public class HDRCreatorSky : SkySettings
    {
        [Tooltip("Specify the latlong texturemap HDRP uses to render the sky.")]
        public RenderTextureParameter skyRenderTexture = new RenderTextureParameter(null);

        public override int GetHashCode()
        {
            int hash = base.GetHashCode();
            unchecked
            {
                hash = skyRenderTexture.value != null ? hash * 23 + skyRenderTexture.GetHashCode() : hash;
            }
            return hash;
        }

        public override Type GetSkyRendererType() => typeof(HDRCreatorSkyRenderer);
    }
}

SkyRenderer (adapt parts from HDRISky

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;

namespace HeadlessStudio.HDRCreator
{
    public static class HelperSky
    {
        public static RenderTexture _sky;
    }

    public class HDRCreatorSkyRenderer : SkyRenderer
    {
        Material m_SkyHDRIMaterial; // Renders a cubemap into a render texture (can be a 3D cube or 2D)
        MaterialPropertyBlock m_PropertyBlock = new MaterialPropertyBlock();

        public HDRCreatorSkyRenderer()
        {
        }

        public override void Build()
        {
            m_SkyHDRIMaterial = CoreUtils.CreateEngineMaterial(Shader.Find("Hidden/HDRCreator/SkyPreview"));
        }

        public override void Cleanup()
        {
            CoreUtils.Destroy(m_SkyHDRIMaterial);
        }

        protected override bool Update(BuiltinSkyParameters builtinParams)
        {
            return base.Update(builtinParams);
        }

        public override void RenderSky(BuiltinSkyParameters builtinParams, bool renderForCubemap, bool renderSunDisk)
        {
            var hdriSky = (HDRCreatorSky)builtinParams.skySettings;
            if (HelperSky._sky != null)
            {
                //m_PropertyBlock.SetTexture("_MainTex", HelperSky._sky);
                m_PropertyBlock.SetTexture("_MainTex", hdriSky.skyRenderTexture.value);
                m_PropertyBlock.SetFloat("_Exposure", hdriSky.exposure.value);
                m_PropertyBlock.SetFloat("_Rotation", -Mathf.Deg2Rad * hdriSky.rotation.value);
            }

            m_PropertyBlock.SetMatrix("_PixelCoordToViewDirWS", builtinParams.pixelCoordToViewDirMatrix);

            CoreUtils.DrawFullScreen(builtinParams.commandBuffer, m_SkyHDRIMaterial, m_PropertyBlock, renderForCubemap ? 0 : 1);
        }
    }
}

And the shader based on panoramic shader

Shader "Hidden/HDRCreator/SkyPreview"
{
    HLSLINCLUDE
   
    #pragma vertex Vert
   
    #pragma editor_sync_compilation
    #pragma target 4.5
    #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
   
    #define LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
   
    #pragma multi_compile _ DEBUG_DISPLAY
    #pragma multi_compile SHADOW_LOW SHADOW_MEDIUM SHADOW_HIGH
   
    #pragma multi_compile USE_FPTL_LIGHTLIST USE_CLUSTERED_LIGHTLIST
   
    #define ATTRIBUTES_NEED_NORMAL
    #define ATTRIBUTES_NEED_TANGENT
    #define VARYINGS_NEED_POSITION_WS
    #define VARYINGS_NEED_TANGENT_TO_WORLD
   
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPass.cs.hlsl"
   
    #define SHADERPASS SHADERPASS_FORWARD_UNLIT
   
    #define HAS_LIGHTLOOP
   
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonLighting.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SDF2D.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariablesFunctions.hlsl"
   
   
    TEXTURE2D(_MainTex);
    SAMPLER(sampler_MainTex);
    half _Exposure;
    float _Rotation;
   
    inline float2 ToRadialCoords(float3 coords)
    {
        float3 normalizedCoords = normalize(coords);
        float latitude = acos(normalizedCoords.y);
        float longitude = atan2(normalizedCoords.z, normalizedCoords.x);
        float2 sphereCoords = float2(longitude, latitude) * float2(0.5 / PI, 1.0 / PI);
        return float2(0.5, 1.0) - sphereCoords;
    }
   
    float3 RotateAroundYInDegrees(float3 vertex, float degrees)
    {
        float alpha = degrees * PI / 180.0;
        float sina, cosa;
        sincos(alpha, sina, cosa);
        float2x2 m = float2x2(cosa, -sina, sina, cosa);
        return float3(mul(m, vertex.xz), vertex.y).xzy;
    }
   
    struct appdata_t
    {
        float4 vertex: POSITION;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };
   
    struct v2f
    {
        float4 vertex: SV_POSITION;
        float3 texcoord: TEXCOORD0;
        UNITY_VERTEX_OUTPUT_STEREO
    };
   
    v2f Vert(appdata_t v)
    {
        v2f o;
        UNITY_SETUP_INSTANCE_ID(v);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
        float3 rotated = RotateAroundYInDegrees(v.vertex, _Rotation);
        o.vertex = TransformWorldToHClip(rotated);
        o.texcoord = v.vertex.xyz;
        return o;
    }
   
    float4 RenderSky(v2f i, float exposure)
    {
        float2 tc = ToRadialCoords(i.texcoord);
        if (tc.x > 1.0)
            return float4(0, 0, 0, 1);
        tc.x = fmod(tc.x, 1);
       
        float4 c = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, tc);
        c *= exposure;
        return float4(c.rgb, 1);
    }
   
    float4 FragBaking(v2f i): SV_Target
    {
        return RenderSky(i, 1.0);
    }
   
    float4 FragRender(v2f i): SV_Target
    {
        return RenderSky(i, _Exposure);
    }
    ENDHLSL
   
    SubShader
    {
        Pass
        {
            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off
           
            HLSLPROGRAM
           
            #pragma fragment FragBaking
            ENDHLSL
           
        }
       
        Pass
        {
            ZWrite Off
            ZTest LEqual
            Blend Off
            Cull Off
           
            HLSLPROGRAM
           
            #pragma fragment FragRender
            ENDHLSL
           
        }
    }
    Fallback Off
}

Just a question, did you actually check that you can access the Volume? Try reading values from it and see if you get correct reads.

On the surface it looks pretty much OK to my eye. Can’t spot the mistake (if there’s any) right away after a long day of coding. hm.

But I just repeat that I’d like to hear that you’ve tested 100% that you get values from the volume and that you get some test values written correctly to the shader, too.

Hi,
Yep pretty much tested, and I get the Volume stuff, and can access it. But it’s easy to test, without even using the volume code. Just replacing the RenderTextureParameter by a TextureParameter, and feeding a normal equirectangular hdri doesn’t show, and the background is all funky.

Cheers,
Daniel

Just out of curiosity I checked a bit more your code; Looks like it’s working all ok (if I remember all the changes I needed to make to get it running here) and I could feed my own render texture in just fine, but the problem is your shader which looks pretty weird and does not render the sky correctly here. Correctly I mean it can’t output anything, just draws something from the rendering process (diffuse camera texture?) to the sky. Is that what you meant with “all funky” ?. So no wonder it’s not working for you.

I think it would be best to adapt your sky rendering to the current templates (the existing skies) in HDRP.

We ended up solving this, as you’ve mentioned, there were some issues with the shader and some other bits. Unfortunately Unity documentation is very outdated and it’s very hard to figure out things without trial and error which is very time consuming. Thanks for your timing looking at this!

Cheers,
Artur Leao