Search Unity

Update built-in Skybox/Panoramic shader to rotate around X,Y,Z Axis

Discussion in 'Shaders' started by Zapan15, Jul 4, 2018.

  1. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    186
    Update built-in Skybox/Panoramic shader to rotate around X,Y,Z Axis

    Hi,

    The built-in shader "Skybox/Panoramic" has the option to rotate around the Y axis.
    Can someone help me to extend this, so it can be rotated around Y and Z, too (also in combination with each other)?

    I know that we need another matrix calculation for that, but currently I’m stuck when combining all axes together within this function: "RotateAroundYInDegrees"

    Please find below the built-in shader:

    Code (CSharp):
    1.  
    2. // Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
    3.  
    4. Shader "Skybox/Panoramic" {
    5. Properties {
    6.     _Tint ("Tint Color", Color) = (.5, .5, .5, .5)
    7.     [Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0
    8.     _Rotation ("Rotation", Range(0, 360)) = 0
    9.     [NoScaleOffset] _MainTex ("Spherical  (HDR)", 2D) = "grey" {}
    10.     [KeywordEnum(6 Frames Layout, Latitude Longitude Layout)] _Mapping("Mapping", Float) = 1
    11.     [Enum(360 Degrees, 0, 180 Degrees, 1)] _ImageType("Image Type", Float) = 0
    12.     [Toggle] _MirrorOnBack("Mirror on Back", Float) = 0
    13.     [Enum(None, 0, Side by Side, 1, Over Under, 2)] _Layout("3D Layout", Float) = 0
    14. }
    15.  
    16. SubShader {
    17.     Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
    18.     Cull Off ZWrite Off
    19.  
    20.     Pass {
    21.  
    22.         CGPROGRAM
    23.         #pragma vertex vert
    24.         #pragma fragment frag
    25.         #pragma target 2.0
    26.         #pragma multi_compile __ _MAPPING_6_FRAMES_LAYOUT
    27.  
    28.         #include "UnityCG.cginc"
    29.  
    30.         sampler2D _MainTex;
    31.         float4 _MainTex_TexelSize;
    32.         half4 _MainTex_HDR;
    33.         half4 _Tint;
    34.         half _Exposure;
    35.         float _Rotation;
    36. #ifndef _MAPPING_6_FRAMES_LAYOUT
    37.         bool _MirrorOnBack;
    38.         int _ImageType;
    39.         int _Layout;
    40. #endif
    41.  
    42. #ifndef _MAPPING_6_FRAMES_LAYOUT
    43.         inline float2 ToRadialCoords(float3 coords)
    44.         {
    45.             float3 normalizedCoords = normalize(coords);
    46.             float latitude = acos(normalizedCoords.y);
    47.             float longitude = atan2(normalizedCoords.z, normalizedCoords.x);
    48.             float2 sphereCoords = float2(longitude, latitude) * float2(0.5/UNITY_PI, 1.0/UNITY_PI);
    49.             return float2(0.5,1.0) - sphereCoords;
    50.         }
    51. #endif
    52.  
    53. #ifdef _MAPPING_6_FRAMES_LAYOUT
    54.         inline float2 ToCubeCoords(float3 coords, float3 layout, float4 edgeSize, float4 faceXCoordLayouts, float4 faceYCoordLayouts, float4 faceZCoordLayouts)
    55.         {
    56.             // Determine the primary axis of the normal
    57.             float3 absn = abs(coords);
    58.             float3 absdir = absn > float3(max(absn.y,absn.z), max(absn.x,absn.z), max(absn.x,absn.y)) ? 1 : 0;
    59.             // Convert the normal to a local face texture coord [-1,+1], note that tcAndLen.z==dot(coords,absdir)
    60.             // and thus its sign tells us whether the normal is pointing positive or negative
    61.             float3 tcAndLen = mul(absdir, float3x3(coords.zyx, coords.xzy, float3(-coords.xy,coords.z)));
    62.             tcAndLen.xy /= tcAndLen.z;
    63.             // Flip-flop faces for proper orientation and normalize to [-0.5,+0.5]
    64.             bool2 positiveAndVCross = float2(tcAndLen.z, layout.x) > 0;
    65.             tcAndLen.xy *= (positiveAndVCross[0] ? absdir.yx : (positiveAndVCross[1] ? float2(absdir[2],0) : float2(0,absdir[2]))) - 0.5;
    66.             // Clamp values which are close to the face edges to avoid bleeding/seams (ie. enforce clamp texture wrap mode)
    67.             tcAndLen.xy = clamp(tcAndLen.xy, edgeSize.xy, edgeSize.zw);
    68.             // Scale and offset texture coord to match the proper square in the texture based on layout.
    69.             float4 coordLayout = mul(float4(absdir,0), float4x4(faceXCoordLayouts, faceYCoordLayouts, faceZCoordLayouts, faceZCoordLayouts));
    70.             tcAndLen.xy = (tcAndLen.xy + (positiveAndVCross[0] ? coordLayout.xy : coordLayout.zw)) * layout.yz;
    71.             return tcAndLen.xy;
    72.         }
    73. #endif
    74.  
    75.         float3 RotateAroundYInDegrees (float3 vertex, float degrees)
    76.         {
    77.             float alpha = degrees * UNITY_PI / 180.0;
    78.             float sina, cosa;
    79.             sincos(alpha, sina, cosa);
    80.             float2x2 m = float2x2(cosa, -sina, sina, cosa);
    81.             return float3(mul(m, vertex.xz), vertex.y).xzy;
    82.         }
    83.  
    84.         struct appdata_t {
    85.             float4 vertex : POSITION;
    86.             UNITY_VERTEX_INPUT_INSTANCE_ID
    87.         };
    88.  
    89.         struct v2f {
    90.             float4 vertex : SV_POSITION;
    91.             float3 texcoord : TEXCOORD0;
    92. #ifdef _MAPPING_6_FRAMES_LAYOUT
    93.             float3 layout : TEXCOORD1;
    94.             float4 edgeSize : TEXCOORD2;
    95.             float4 faceXCoordLayouts : TEXCOORD3;
    96.             float4 faceYCoordLayouts : TEXCOORD4;
    97.             float4 faceZCoordLayouts : TEXCOORD5;
    98. #else
    99.             float2 image180ScaleAndCutoff : TEXCOORD1;
    100.             float4 layout3DScaleAndOffset : TEXCOORD2;
    101. #endif
    102.             UNITY_VERTEX_OUTPUT_STEREO
    103.         };
    104.  
    105.         v2f vert (appdata_t v)
    106.         {
    107.             v2f o;
    108.             UNITY_SETUP_INSTANCE_ID(v);
    109.             UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
    110.             float3 rotated = RotateAroundYInDegrees(v.vertex, _Rotation);
    111.             o.vertex = UnityObjectToClipPos(rotated);
    112.             o.texcoord = v.vertex.xyz;
    113. #ifdef _MAPPING_6_FRAMES_LAYOUT
    114.             // layout and edgeSize are solely based on texture dimensions and can thus be precalculated in the vertex shader.
    115.             float sourceAspect = float(_MainTex_TexelSize.z) / float(_MainTex_TexelSize.w);
    116.             // Use the halfway point between the 1:6 and 3:4 aspect ratios of the strip and cross layouts to
    117.             // guess at the correct format.
    118.             bool3 aspectTest =
    119.                 sourceAspect >
    120.                 float3(1.0, 1.0f / 6.0f + (3.0f / 4.0f - 1.0f / 6.0f) / 2.0f, 6.0f / 1.0f + (4.0f / 3.0f - 6.0f / 1.0f) / 2.0f);
    121.             // For a given face layout, the coordinates of the 6 cube faces are fixed: build a compact representation of the
    122.             // coordinates of the center of each face where the first float4 represents the coordinates of the X axis faces,
    123.             // the second the Y, and the third the Z. The first two float componenents (xy) of each float4 represent the face
    124.             // coordinates on the positive axis side of the cube, and the second (zw) the negative.
    125.             // layout.x is a boolean flagging the vertical cross layout (for special handling of flip-flops later)
    126.             // layout.yz contains the inverse of the layout dimensions (ie. the scale factor required to convert from
    127.             // normalized face coords to full texture coordinates)
    128.             if (aspectTest[0]) // horizontal
    129.             {
    130.                 if (aspectTest[2])
    131.                 { // horizontal strip
    132.                     o.faceXCoordLayouts = float4(0.5,0.5,1.5,0.5);
    133.                     o.faceYCoordLayouts = float4(2.5,0.5,3.5,0.5);
    134.                     o.faceZCoordLayouts = float4(4.5,0.5,5.5,0.5);
    135.                     o.layout = float3(-1,1.0/6.0,1.0/1.0);
    136.                 }
    137.                 else
    138.                 { // horizontal cross
    139.                     o.faceXCoordLayouts = float4(2.5,1.5,0.5,1.5);
    140.                     o.faceYCoordLayouts = float4(1.5,2.5,1.5,0.5);
    141.                     o.faceZCoordLayouts = float4(1.5,1.5,3.5,1.5);
    142.                     o.layout = float3(-1,1.0/4.0,1.0/3.0);
    143.                 }
    144.             }
    145.             else
    146.             {
    147.                 if (aspectTest[1])
    148.                 { // vertical cross
    149.                     o.faceXCoordLayouts = float4(2.5,2.5,0.5,2.5);
    150.                     o.faceYCoordLayouts = float4(1.5,3.5,1.5,1.5);
    151.                     o.faceZCoordLayouts = float4(1.5,2.5,1.5,0.5);
    152.                     o.layout = float3(1,1.0/3.0,1.0/4.0);
    153.                 }
    154.                 else
    155.                 { // vertical strip
    156.                     o.faceXCoordLayouts = float4(0.5,5.5,0.5,4.5);
    157.                     o.faceYCoordLayouts = float4(0.5,3.5,0.5,2.5);
    158.                     o.faceZCoordLayouts = float4(0.5,1.5,0.5,0.5);
    159.                     o.layout = float3(-1,1.0/1.0,1.0/6.0);
    160.                 }
    161.             }
    162.             // edgeSize specifies the minimum (xy) and maximum (zw) normalized face texture coordinates that will be used for
    163.             // sampling in the texture. Setting these to the effective size of a half pixel horizontally and vertically
    164.             // effectively enforces clamp mode texture wrapping for each individual face.
    165.             o.edgeSize.xy = _MainTex_TexelSize.xy * 0.5 / o.layout.yz - 0.5;
    166.             o.edgeSize.zw = -o.edgeSize.xy;
    167. #else // !_MAPPING_6_FRAMES_LAYOUT
    168.             // Calculate constant horizontal scale and cutoff for 180 (vs 360) image type
    169.             if (_ImageType == 0)  // 360 degree
    170.                 o.image180ScaleAndCutoff = float2(1.0, 1.0);
    171.             else  // 180 degree
    172.                 o.image180ScaleAndCutoff = float2(2.0, _MirrorOnBack ? 1.0 : 0.5);
    173.             // Calculate constant scale and offset for 3D layouts
    174.             if (_Layout == 0) // No 3D layout
    175.                 o.layout3DScaleAndOffset = float4(0,0,1,1);
    176.             else if (_Layout == 1) // Side-by-Side 3D layout
    177.                 o.layout3DScaleAndOffset = float4(unity_StereoEyeIndex,0,0.5,1);
    178.             else // Over-Under 3D layout
    179.                 o.layout3DScaleAndOffset = float4(0, 1-unity_StereoEyeIndex,1,0.5);
    180. #endif
    181.             return o;
    182.         }
    183.  
    184.         fixed4 frag (v2f i) : SV_Target
    185.         {
    186. #ifdef _MAPPING_6_FRAMES_LAYOUT
    187.             float2 tc = ToCubeCoords(i.texcoord, i.layout, i.edgeSize, i.faceXCoordLayouts, i.faceYCoordLayouts, i.faceZCoordLayouts);
    188. #else
    189.             float2 tc = ToRadialCoords(i.texcoord);
    190.             if (tc.x > i.image180ScaleAndCutoff[1])
    191.                 return half4(0,0,0,1);
    192.             tc.x = fmod(tc.x*i.image180ScaleAndCutoff[0], 1);
    193.             tc = (tc + i.layout3DScaleAndOffset.xy) * i.layout3DScaleAndOffset.zw;
    194. #endif
    195.  
    196.             half4 tex = tex2D (_MainTex, tc);
    197.             half3 c = DecodeHDR (tex, _MainTex_HDR);
    198.             c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;
    199.             c *= _Exposure;
    200.             return half4(c, 1);
    201.         }
    202.         ENDCG
    203.     }
    204. }
    205.  
    206.  
    207. CustomEditor "SkyboxPanoramicShaderGUI"
    208. Fallback Off
    209.  
    210. }
    211.  
    Please find the file also attached to this post.


    Thank you :)
     

    Attached Files:

    DragonCoder likes this.
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Search for how to convert a Euler rotation to a rotation matrix.
     
  3. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    186
    Thank you for the Tip!
    It's working fine.
    If someone is interesed in an updated shader, just send me a message :)
     
  4. Shane_Michael

    Shane_Michael

    Joined:
    Jul 8, 2013
    Posts:
    158
    Found this on Google, but no one posted the actual solution.

    So here it is for those who Google this in the future. Remember to normalize the rotation axis if you are setting it to an arbitrary angle. Or don't if you want a weird warping effect.

    Not exhaustively tested, but it seems to work for each axis so I assume I didn't make a typo in the math.

    Code (csharp):
    1.  
    2.     Properties{
    3.         _Axis("Rotation Axis", Vector) = (0, 1, 0, 0)
    4.     }
    5.  
    6.     float3 _Axis;
    7.  
    8.     float3 RotateAroundAxisInDegrees(float3 vertex, float degrees)
    9.     {      
    10.         float alpha = degrees * UNITY_PI / 180.0;
    11.         float sina, cosa;
    12.         sincos(alpha, sina, cosa);
    13.         float oc = 1.0 - cosa;
    14.         float3 a = _Axis * sina;
    15.         float3x3 p = float3x3(_Axis.x*_Axis, _Axis.y*_Axis, _Axis.z*_Axis);
    16.         float3x3 q = float3x3(cosa, -a.z, a.y, a.z, cosa, -a.x, -a.y, a.x, cosa);
    17.         float3x3 m = p * oc + q;      
    18.         return mul(m, vertex);
    19.     }
    20.  
    [Reference]
     
    bgolus likes this.
  5. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    186
    Please find attached the file.

    You can rotate the panorama with the following commands:

    Code (CSharp):
    1.                
    2. Quaternion rot = Quaternion.Euler(newRotation);
    3. Matrix4x4 m = Matrix4x4.TRS(Vector3.zero, rot, new Vector3(1, 1, 1));
    4. targetMaterial.SetMatrix("_Rotation", m);
     

    Attached Files:

  6. adamp990

    adamp990

    Joined:
    Feb 27, 2020
    Posts:
    1
    Maybe it's just me, but your shader does not work at all
     
  7. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    186
    Can you be more precise what exactly is not working? With which Unity Version?
     
  8. TSCSimulation_AH

    TSCSimulation_AH

    Joined:
    Dec 7, 2017
    Posts:
    7
    Can confirm, not working in Unity 2020.1.13f1.
     
  9. Zapan15

    Zapan15

    Joined:
    Apr 11, 2011
    Posts:
    186
    I have not tested it yet with 2020.X, but will do that once it is LTS