Search Unity

Pixel ray direction in frag shader

Discussion in 'Image Effects' started by HotRhodium, Jan 13, 2020.

  1. HotRhodium

    HotRhodium

    Joined:
    Jan 13, 2020
    Posts:
    2
    Hey Unity Peoples,

    This is my first post and I have about 2 months working with Unity at this point. I really think this is a simple problem and I was hoping I could ask the experts. I have no doubt I am doing something really dumb.

    What I am trying to do is run a pseudo "ray marching" algorithm on top of my existing game. This way I can do fog, glass coloration, lighting and some other volumetric effects. I am using an image effect shader on a camera because this will basically go over the screen and fill in the spaces I want with the fog etc. The only problem is I can't get the angle of a ray from each pixel correct.

    I have gone through several awesome tutorials (Like this one) and was able to get one working but the way it worked was by placing a quad in front of the camera. This of course obstructed the rest of the game lol. Others were unlit shaders on objects not on the camera so also not what I'm looking for. All I want is to have a float3 with the direction of the ray from that pixel in my fragment shader. Obviously in world space corrected for camera rotation etc. I think I can do the rest of my trig from there. Here is what I have...

    HLSL
    Code (CSharp):
    1. Shader "RayShader/RayShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         //_CamAng;
    7.         //_FOV;
    8.         //_FOVXAng;
    9.         //_FOVYAng;
    10.     }
    11.     SubShader
    12.     {
    13.         // No culling or depth
    14.         Cull Off ZWrite Off ZTest Always
    15.  
    16.         Pass
    17.         {
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.  
    22.             #include "UnityCG.cginc"
    23.  
    24.             struct appdata
    25.             {
    26.                 float4 vertex : POSITION;
    27.                 float2 uv : TEXCOORD0;
    28.             };
    29.  
    30.             struct v2f
    31.             {
    32.                 float2 uv : TEXCOORD0;
    33.                 float4 vertex : SV_POSITION;
    34.             };
    35.  
    36.             v2f vert (appdata v)
    37.             {
    38.                 v2f o;
    39.                 o.vertex = UnityObjectToClipPos(v.vertex);
    40.                 o.uv = v.uv;
    41.                 return o;
    42.             }
    43.  
    44.             sampler2D _MainTex;
    45.  
    46.             uniform float3 _CamAng;
    47.             uniform float3 _FOV;
    48.             uniform float2 _FOVXAng;
    49.             uniform float2 _FOVYAng;
    50.  
    51.             fixed4 frag (v2f i) : SV_Target
    52.             {
    53.                 fixed4 col = tex2D(_MainTex, i.uv);
    54.  
    55.                 float3 WorldSpaceDirection = 0;//This is where i want the angle of a ray from this pixel in world space!
    56.  
    57.                 col = 0.5;
    58.                 float PixXAng = lerp(_FOVYAng.y, _FOVYAng.x, i.uv.x);//This is junk.
    59.                 float PixYAng = lerp(_FOVXAng.y, _FOVXAng.x, i.uv.y);//Im just grasping at straws at this point.
    60.                 PixXAng = (sin(PixXAng) * 0.5) + 0.5;
    61.                 PixYAng = (sin(PixYAng) * 0.5) + 0.5;
    62.  
    63.                 col *= fixed4(PixXAng, PixYAng,0, 0);
    64.                 return col;
    65.             }
    66.             ENDCG
    67.         }
    68.     }
    69. }
    70.  
    C#
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. [ExecuteInEditMode]
    6. public class CamToShader : MonoBehaviour
    7. {
    8.   public Material MyMat;
    9.   public Shader MyShade;
    10.   public Camera MyCamera;
    11.  
    12.   private Vector3 _CamAng;
    13.   private Vector3 _FOV;
    14.   private Vector2 _FOVXAng;
    15.   private Vector2 _FOVYAng;
    16.  
    17.   private Vector2 Flip = new Vector2(-1, 1);
    18.   private Vector2 TempAng;
    19.  
    20.   void OnRenderImage(RenderTexture source, RenderTexture destination)
    21.   {
    22.     if (!MyMat)
    23.     {
    24.       Graphics.Blit(source, destination);
    25.       return;
    26.     }
    27.     else
    28.     {
    29.       _CamAng = Mathf.Deg2Rad * MyCamera.transform.eulerAngles;
    30.       _FOV.y = Mathf.Deg2Rad * MyCamera.fieldOfView * 0.5f;
    31.       _FOV.x = _FOV.y * MyCamera.aspect;
    32.  
    33.       _FOVXAng = new Vector2(-_FOV.x, _FOV.x);
    34.       _FOVXAng += new Vector2(_CamAng.x, _CamAng.x);
    35.  
    36.       _FOVYAng = new Vector2(-_FOV.y, _FOV.y);
    37.       _FOVYAng += new Vector2(_CamAng.y, _CamAng.y);
    38.  
    39.       MyMat.SetVector("_CamAng", _CamAng);
    40.       MyMat.SetVector("_FOV", _FOV);
    41.       MyMat.SetVector("_FOVXAng", _FOVXAng);
    42.       MyMat.SetVector("_FOVYAng", _FOVYAng);
    43.  
    44.       Graphics.Blit(source, destination, MyMat);
    45.       return;
    46.     }
    47.   }
    48. }
    I was very excited when I got this together because at first the colors on the screen tracked with the camera rotation but if I rotate beyond straight up the colors flip and camera rotation on z does nothing. Is there a built in shaderlab variable that has the vector of each frag?

    Anything will help thanks in advance. I've got to head to bed for work but I appreciate any help and will check in tomorrow.
     
  2. HotRhodium

    HotRhodium

    Joined:
    Jan 13, 2020
    Posts:
    2
    I guess that was a dumb question. No problem, for the next person I was expecting to get Euler angles and then convert them to vectors but that was a mess (math vectors not datatype). I ended up just getting 4 vectors from the frustum and then I passed them to the shader like this.

    Code (CSharp):
    1.     Vector3[] FrustumCorners = new Vector3[4];
    2.     Vector3[] NormCorners = new Vector3[4];
    3.    
    4.     MyCam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), MyCam.farClipPlane, Camera.MonoOrStereoscopicEye.Mono, FrustumCorners);
    5.     for (int i = 0; i < 4; i++)
    6.     {
    7.       NormCorners[i] = MyCam.transform.TransformVector(FrustumCorners[i]);
    8.       NormCorners[i] = NormCorners[i].normalized;
    9.     }
    10.     Debug.DrawRay(new Vector3(0, 0, 0), NormCorners[0], Color.red);   //BL
    11.     Debug.DrawRay(new Vector3(0, 0, 0), NormCorners[1], Color.blue);  //TL
    12.     Debug.DrawRay(new Vector3(0, 0, 0), NormCorners[2], Color.yellow);//TR
    13.     Debug.DrawRay(new Vector3(0, 0, 0), NormCorners[3], Color.green); //BR
    14.     MyMaterial.SetVector("_BL", NormCorners[0]);
    15.     MyMaterial.SetVector("_TL", NormCorners[1]);
    16.     MyMaterial.SetVector("_TR", NormCorners[2]);
    17.     MyMaterial.SetVector("_BR", NormCorners[3]);
    Then in the shader I do a lerp on the vectors like an H and normalize like this.

    Code (CSharp):
    1.             float4 _BL;
    2.             float4 _TL;
    3.             float4 _TR;
    4.             float4 _BR;
    5.  
    6.             float4 NormalLeft;
    7.             float4 NormalRight;
    8.             float4 NormalH;
    9.  
    10.  
    11.             fixed4 frag (v2f i) : SV_Target
    12.             {
    13.                 fixed4 col = tex2D(_MainTex, i.uv);
    14.                 NormalLeft = lerp(_BL, _TL,i.uv.y);
    15.                 NormalRight = lerp(_BR, _TR, i.uv.y);
    16.                 NormalH = lerp(NormalLeft, NormalRight,i.uv.x);
    17.                 NormalH = normalize(NormalH);
    Then I just add the camera position to that and I have a vector. Stepping from camera position along this vector produced what looks like correct results. ie

    SamplePoint += CamPos + (NormalH *1)
    SamplePoint += CamPos + (NormalH *2)
    SamplePoint += CamPos + (NormalH *3)

    I'm sure there are better ways but this is fast cheap and all I needed. Seems to work with FOV changes and any rotation x,y or z. Thanks.
     
  3. 8bitgoose

    8bitgoose

    Joined:
    Dec 28, 2014
    Posts:
    448
    This was not a dumb question. I am looking for the exact same thing. Although I feel like there should be an easier way to do this directly in the shader.
     
  4. wwWwwwW1

    wwWwwwW1

    Joined:
    Oct 31, 2021
    Posts:
    769
    Hi, I think you are looking for view direction.

    If you are using shader graph, you can use the view direction node (not view vector node) directly.

    For custom shaders, it's normalize(camera position - vertex or pixel's position).

    The global variable "_WorldSpaceCameraPos" contains the camera's positionWS. (see documentation)
     
    Last edited: Dec 9, 2022
  5. 8bitgoose

    8bitgoose

    Joined:
    Dec 28, 2014
    Posts:
    448
    Wow, once again @wwWwwwW1 you have come to my rescue.

    I am so bad at shaders I had to learn the simplest stuff just to get this working.

    I had to add this line in the vert method

    result.worldSpacePos = mul(unity_ObjectToWorld, input.positionOS).xyz;


    which adds a world space to each fragment (I think).

    And then added this to the frag shader

    float3 worldCamPos = _WorldSpaceCameraPos;
    float3 direction = normalize(input.worldSpacePos.xyz - _WorldSpaceCameraPos);
     
    wwWwwwW1 likes this.