# HLSL Converting View Direction into Equiangular UVs

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

]4
Test Pattern

*(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

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
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

{
// 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
}
}
}


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) :

Actual result:

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

///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
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

{
// 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
}