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

Hi, I’m writing a shader that assigns to the pixels color the corresponding world position.

Shader

Shader "Custom/Uvs_to_WorldPositions"
{
    Properties
    {
        _MainTex("Base (RGB)", 2D) = "white" {}
    }

        SubShader
    {
        Tags{ "RenderType" = "Opaque" }

        CGPROGRAM
    #pragma surface surf Unlit

    sampler2D _MainTex; 

    struct Input
    {
        float2 uv_MainTex;
        float3 worldPos;
    };

    half4 LightingUnlit(SurfaceOutput s, half3 lightDir, half atten)
    {
        return half4(s.Albedo, s.Alpha);
    }

    void surf(Input IN, inout SurfaceOutput o)
    {
        float3 col = IN.worldPos;
        o.Albedo = col.rgb;
    }
    ENDCG
    }
        Fallback "Diffuse"
}

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

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

1 Like

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:

Shader

Shader "Custom/UVs_to_WorldPositions" {
    SubShader{
        Pass{

            Lighting Off
            Cull Off

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct v2f {
                    float4 pos : SV_POSITION;
                    fixed4 color : COLOR;
                };

                // vertex input: position, UV
                struct appdata {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };

                v2f vert(appdata v){
                    v2f o;
                    float4 p = v.vertex;
                    float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
                    o.pos = float4(v.uv.x * 2.0 - 1.0, v.uv.y * 2.0 - 1.0, 1.0, 1.0);
                    o.color.xyz = worldPos;
                    o.color.w = 1.0;
                    return o;      
                }

                fixed4 frag(v2f i) : SV_Target{
                    return i.color;
                }
            ENDCG
        }
    }
}

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

2984279--222016--upload_2017-3-7_11-4-3.png

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

Shader for World Positions

Shader "Custom/UV_to_WorldPos" {
    SubShader{
        Pass{

            Lighting Off
            Cull Off

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"

                struct v2f {
                    float4 pos : SV_POSITION;
                    float4 color : COLOR;
                    float4 worldSpacePosition : TEXCOORD0;
                };

                // vertex input: position, UV
                struct appdata {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };

                v2f vert(appdata v){
                    v2f o;
                    float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
                    o.worldSpacePosition = mul(unity_ObjectToWorld, v.vertex);
                    o.pos = float4(v.uv.x * 2.0 - 1.0, v.uv.y * 2.0 - 1.0, 1.0, 1.0);
                    return o;     
                }

                float4 frag(v2f i) : SV_Target{
                    return i.worldSpacePosition;
                }
            ENDCG
        }
    }
}

Shader for Normals

Shader "Custom/UV_to_Normal" {
    SubShader{
        Pass{

        Lighting Off
        Cull Off

        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "UnityCG.cginc"

        struct v2f {
        float4 pos : SV_POSITION;
        fixed4 color : COLOR;
    };

    // vertex input: normal, UV
    struct appdata {
        float4 normal : NORMAL;
        float2 uv : TEXCOORD0;
    };



    v2f vert(appdata v) {
        v2f o;
        float3 worldNormal = UnityObjectToWorldNormal(v.normal);
        o.pos = float4(v.uv.x * 2.0 - 1.0, v.uv.y * 2.0 - 1.0, 1.0, 1.0);
        o.color.xyz = (worldNormal*0.5)+0.5;
        o.color.w = 1.0;
        return o;
    }

    fixed4 frag(v2f i) : SV_Target{
        return i.color;
    }
        ENDCG
    }
    }
}
  • 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.


I don’t know if I was clear explaining this issue!! Do you have some idea of what’s going on?

1 Like

Are the textures you’re creating using RGBAFloat?

1 Like

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! :slight_smile:

1 Like