1
\$\begingroup\$

In Unity, I am attempting to create a custom skybox that use a single render texture to create a flat unmoving skybox (the render texture source will do it's own movement) and reflect off objects as if it were an equiangular cubemap. So far using the unity custom skybox documentation I have been able to create a skybox with a flat image, however I have no idea how to read from the 2D render texture as if it were an equiangular cubemap. I've tried just using same UV coordinates from the skybox rendering pass as the reflection pass, but this results in a very small tiled reflection that looks very poor. I have also tried converting the view direction into cubemap uvs but this has the same result but with the entire image tiled.

What I am basically looking for is one of the following (but I am open to suggestions for better approaches):
1.) A way I can convert a float3 view direction into the uv coordinates needed to read from my image as if it were an equiangular cubemap.
2.) A way to convert the texture2D into an equiangular cubemap in real time every frame.

Here are some images I have taken of my current progress to show what I am looking for:
Custom Skybox:
Background Correct.
Reflection Wrong



















HDRI Skybox:
Background Wrong.
Reflection Correct



















Sample Image
enter image description here
enter image description here]4
Test Pattern
enter image description here
enter image description here
*(apologies for the crazy table, turns out markdown doesn't work as advertised on StackExchange.)


Although this may not be helpful here is some of the shader code I am working on for reference:
Shader "Hidden/DontTouchMyStuff_OkayOnlyALittle_BeGentle/HDRP/Skyboxes/HdriFlatSky"
{
    HLSLINCLUDE

    // Some vertex shader stuff here, don't worry about it.

    ///////////////////////////////////////////////////////////////////
    // FRAGMENT SHADER
    ///////////////////////////////////////////////////////////////////
    float4 GetTextureColor(float4 screenPixelPosition)
    {
        return tex2D(_MainTex, float2(screenPixelPosition.x / _ScreenParams.x, screenPixelPosition.y / _ScreenParams.y));
    }

    // Renders reflected cubemap
    float4 FragBaking(Varyings input) : SV_Target
    {
        return GetTextureColor(input.positionCS); // tex2D(_MainTex, float2(4 * ((input.positionCS.x / _ScreenParams.x)), 4 * (1 - (input.positionCS.y / _ScreenParams.y))));
    }

    // renders sky, works perfectly so far.
    float4 FragRender(Varyings input) : SV_Target
    {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
        float4 color = GetTextureColor(input.positionCS);
        color.rgb *= 1;// GetCurrentExposureMultiplier();
        return color;
    }
        
    // experiment to use direction as uvs. Didn't work, results in reflection like texture is every side of a cubemap.
    float4 Frag(Varyings input) : SV_Target
    {
        // Points towards the camera
        float3 viewDirWS = normalize(mul(float3(input.positionCS.xy, 1.0), (float3x3)_PixelCoordToViewDirWS));
        // Reverse it to point into the scene
        float3 dir = -viewDirWS;

        // Rotate direction
        float phi = 0.0;
        float cosPhi, sinPhi;
        sincos(phi, sinPhi, cosPhi);
        float3 rotDirX = float3(cosPhi, 0, -sinPhi);
        float3 rotDirY = float3(sinPhi, 0, cosPhi);
        dir = float3(dot(rotDirX, dir), dir.y, dot(rotDirY, dir));
        
        float3 dirAbs = abs(dir);
        float ma = 0;
        float2 uv = float2(0, 0);
        
        if (dirAbs.z >= dirAbs.x && dirAbs.z >= dirAbs.y)
        {
            ma = 0.5 / dirAbs.z;
            uv = float2(dir.z < 0.0 ? -dir.x : dir.x, dir.y);
        }
        else if (dirAbs.y >= dirAbs.x)
        {
            ma = 0.5 / dirAbs.y;
            uv = float2(dir.x, dir.y < 0.0 ? -dir.z : dir.z);
        }
        else
        {
            ma = 0.5 / dirAbs.x;
            uv = float2(dir.x < 0.0 ? dir.z : -dir.z, dir.y);
        }
        
        return float4(tex2D(_MainTex, uv * ma + 0.5).rgb, 1.0);
    }

    ENDHLSL

    SubShader
    {
        // For cubemap
        Pass
        {
            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FragBaking
            ENDHLSL
        }

        // For fullscreen Sky
        Pass
        {
            ZWrite Off
            ZTest LEqual
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FragRender
            ENDHLSL
        }
    }
}
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

As with most things in life, I seem to have found the answer almost immediately after asking for help (but not during the 3 days I spent trying to figure it out before hand). Using the formula for spherically mapping textures provided by Stephane Hockenhull I was able to substitute the theoretical spheres normal with my view direction vector to produce a similar result to what I was looking for in my cube map. After that I simply remapped the uvs (or specially just the u value) from the range of [0, 1] to, erm well, [0, 1], but with the added twist that [0, 0.5] is now mapped to [0, 1] and [0.5, 1] is mapped to [1, 0] (basically duplicate and flip the input texture. For reference please see the expected result, final result, and shader script below. As you can see the UVs are slightly off but close enough in this case where I think it is passible. If anyone knows a better algorithm I would be happy to mark them as the true correct answer.

Expected result (single image for Texture2D background, and image+mirrored image as equiangular cubemap texture) : enter image description here

Actual result: enter image description here

Shader Code:

Shader "Hidden/CosmicLab/HDRP/Skyboxes/HdriFlatSky2"
{
    HLSLINCLUDE

        ///////////////////////////////////////////////////////////////////
        // FRAGMENT SHADER
        ///////////////////////////////////////////////////////////////////
        float4 FragBaking(Varyings input) : SV_Target
        {
            float3 viewDirWS = GetSkyViewDirWS(input.positionCS.xy);

            // Reverse it to point into the scene
            float3 dir = -viewDirWS;

            // Spherically map UVs
            float u = atan2(dir.x, dir.z) / TWO_PI + 0.5;
            float v = dir.y * 0.5 + 0.5;

            if (u > 0.5)
            {
                u = 1 - u;
            }

            return tex2D(_MainTex, float2(2 * u, v));
        }

        float4 FragRender(Varyings input) : SV_Target
        {
            UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
            float4 color = tex2D(_MainTex, float2(input.positionCS.x / _ScreenParams.x, input.positionCS.y / _ScreenParams.y));
            color.rgb *= 1;// GetCurrentExposureMultiplier();
            return color;
        }
        
    ENDHLSL

    SubShader
    {
        // For cubemap
        Pass
        {
            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FragBaking
            ENDHLSL
        }

        // For fullscreen Sky
        Pass
        {
            ZWrite Off
            ZTest LEqual
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FragRender
            ENDHLSL
        }
    }

    Fallback Off
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .