Search Unity

Writing a shader to write uv's world positions on a texture

Discussion in 'Shaders' started by fra3point, Mar 6, 2017.

  1. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    269
    Hi, I'm writing a shader that assigns to the pixels color the corresponding world position.

    Code (csharp):
    1. Shader "Custom/Uvs_to_WorldPositions"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex("Base (RGB)", 2D) = "white" {}
    6.     }
    7.  
    8.         SubShader
    9.     {
    10.         Tags{ "RenderType" = "Opaque" }
    11.  
    12.         CGPROGRAM
    13.     #pragma surface surf Unlit
    14.  
    15.     sampler2D _MainTex;
    16.  
    17.     struct Input
    18.     {
    19.         float2 uv_MainTex;
    20.         float3 worldPos;
    21.     };
    22.  
    23.     half4 LightingUnlit(SurfaceOutput s, half3 lightDir, half atten)
    24.     {
    25.         return half4(s.Albedo, s.Alpha);
    26.     }
    27.  
    28.     void surf(Input IN, inout SurfaceOutput o)
    29.     {
    30.         float3 col = IN.worldPos;
    31.         o.Albedo = col.rgb;
    32.     }
    33.     ENDCG
    34.     }
    35.         Fallback "Diffuse"
    36. }

    My objective is to write a texture in which each pixel represents a uv coordinate and the pixel's color represents the world position.
    For example, pixel (x,y) represents the uv coordinates (xxx,yyy).

    So, I basically need to write a texture with this shader output.
    I know how to use the Blit() function, but it won't help here because of the world positions (and obviously, Blit() can't handle them).

    How can I do?

    Thanks,
    Francesco
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    You need a shader that outputs the clip position as the UVs.

    o.pos = float4(v.uv * 2.0 - 1.0, 0.0, 1.0);
     
    fra3point likes this.
  3. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    269
    Hi @bgolus , thanks for the answer... it led me to the solution!

    The right idea was:
    - "Flatten" the vertices basing on their uv coordinates by modifying the o.pos property
    - Set output color basing on the original vertex position
    - Render the resulting screen space square and save the texture

    This is the vertex/fragment shader which outputs the world position of the uv:

    Code (csharp):
    1.  
    2. Shader "Custom/UVs_to_WorldPositions" {
    3.     SubShader{
    4.         Pass{
    5.  
    6.             Lighting Off
    7.             Cull Off
    8.  
    9.             CGPROGRAM
    10.                 #pragma vertex vert
    11.                 #pragma fragment frag
    12.                 #include "UnityCG.cginc"
    13.  
    14.                 struct v2f {
    15.                     float4 pos : SV_POSITION;
    16.                     fixed4 color : COLOR;
    17.                 };
    18.  
    19.                 // vertex input: position, UV
    20.                 struct appdata {
    21.                     float4 vertex : POSITION;
    22.                     float2 uv : TEXCOORD0;
    23.                 };
    24.  
    25.                 v2f vert(appdata v){
    26.                     v2f o;
    27.                     float4 p = v.vertex;
    28.                     float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    29.                     o.pos = float4(v.uv.x * 2.0 - 1.0, v.uv.y * 2.0 - 1.0, 1.0, 1.0);
    30.                     o.color.xyz = worldPos;
    31.                     o.color.w = 1.0;
    32.                     return o;      
    33.                 }
    34.  
    35.                 fixed4 frag(v2f i) : SV_Target{
    36.                     return i.color;
    37.                 }
    38.             ENDCG
    39.         }
    40.     }
    41. }

    And here's its output on a render texture (the mesh is a primitive sphere):

    upload_2017-3-7_11-4-3.png
     
  4. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    269
    "Houston, we have a problem...." @bgolus

    I summarize what I'm doing:

    - Given a 3D model, I create 2 textures (uncompressed, read/write enabled, point filtering).
    - To create them I use two shaders and then a ReadPixles():

    Code (csharp):
    1. Shader "Custom/UV_to_WorldPos" {
    2.     SubShader{
    3.         Pass{
    4.  
    5.             Lighting Off
    6.             Cull Off
    7.  
    8.             CGPROGRAM
    9.                 #pragma vertex vert
    10.                 #pragma fragment frag
    11.                 #include "UnityCG.cginc"
    12.  
    13.                 struct v2f {
    14.                     float4 pos : SV_POSITION;
    15.                     float4 color : COLOR;
    16.                     float4 worldSpacePosition : TEXCOORD0;
    17.                 };
    18.  
    19.                 // vertex input: position, UV
    20.                 struct appdata {
    21.                     float4 vertex : POSITION;
    22.                     float2 uv : TEXCOORD0;
    23.                 };
    24.  
    25.                 v2f vert(appdata v){
    26.                     v2f o;
    27.                     float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    28.                     o.worldSpacePosition = mul(unity_ObjectToWorld, v.vertex);
    29.                     o.pos = float4(v.uv.x * 2.0 - 1.0, v.uv.y * 2.0 - 1.0, 1.0, 1.0);
    30.                     return o;    
    31.                 }
    32.  
    33.                 float4 frag(v2f i) : SV_Target{
    34.                     return i.worldSpacePosition;
    35.                 }
    36.             ENDCG
    37.         }
    38.     }
    39. }
    Code (csharp):
    1. Shader "Custom/UV_to_Normal" {
    2.     SubShader{
    3.         Pass{
    4.  
    5.         Lighting Off
    6.         Cull Off
    7.  
    8.         CGPROGRAM
    9.         #pragma vertex vert
    10.         #pragma fragment frag
    11.         #include "UnityCG.cginc"
    12.  
    13.         struct v2f {
    14.         float4 pos : SV_POSITION;
    15.         fixed4 color : COLOR;
    16.     };
    17.  
    18.     // vertex input: normal, UV
    19.     struct appdata {
    20.         float4 normal : NORMAL;
    21.         float2 uv : TEXCOORD0;
    22.     };
    23.  
    24.  
    25.  
    26.     v2f vert(appdata v) {
    27.         v2f o;
    28.         float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    29.         o.pos = float4(v.uv.x * 2.0 - 1.0, v.uv.y * 2.0 - 1.0, 1.0, 1.0);
    30.         o.color.xyz = (worldNormal*0.5)+0.5;
    31.         o.color.w = 1.0;
    32.         return o;
    33.     }
    34.  
    35.     fixed4 frag(v2f i) : SV_Target{
    36.         return i.color;
    37.     }
    38.         ENDCG
    39.     }
    40.     }
    41. }

    - Texture A, which in each pixel stores the world position of a point of the model. (I assume that the model points have (x,y,z) in the range (0,1).
    - Texture B, which in each pixel stores the normal of a point of the model, normalized to (0,1).
    - A script reads one by one the pixels of both texture A and texture B (respectively: Pa and Pb), and it creates a ray.
    - The ray's origin is a Vector3 in which the (x,y,z) values are Pa's (r,g,b) values.
    - The ray's direction is a Vector3 in which the (x,y,z) values are Pb's (r,g,b) values.

    The problem is that some of the rays don't start in the right position. I really don't know why....
    I think there may be 2 reasons:

    1) The texture A contains some wrong values (due to float precision?)
    2) When reading the textures, the GetPixel() function doesn't return the right, precise color.

    upload_2017-3-9_17-56-12.png
    upload_2017-3-9_18-17-12.png

    I don't know if I was clear explaining this issue!! Do you have some idea of what's going on?
     
    Last edited: Mar 9, 2017
    Agent0023 likes this.
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,348
    Are the textures you're creating using RGBAFloat?
     
    fra3point likes this.
  6. fra3point

    fra3point

    Joined:
    Aug 20, 2012
    Posts:
    269
    No, they were ARGB32 (8bit per channel), so the precision was too low. Changing to RGBAFloat did the trick!

    Thanks a lot, @bgolus!

    P.S. Sorry for abusing your help so often, I am very grateful to you! :)
     
    Agent0023 likes this.