Black color if outside of UV coordinates

Hi,

I have to display bitmaps on a tv screen object…but need to be sure that the aspect ratio is preserved
Image to place:

How it display on a square mesh:

So I wrote some code to adjust UV tiling and offset for each image that will be projected on screen:

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class MainCameraScript : MonoBehaviour
{
    public GameObject screen;
    public Material bmp;
    // Use this for initialization
    void Start()
    {
        SetTexture("C:\\testimage.png");
    }

    void SetTexture(string path)
    {
        // Create new texture2d
        Texture2D tex = LoadPNG(path);
        if (tex == null)
        {
            return;
        }

        // Set width/height scale/offset for UV's
        float twidth = 1.0f;
        float theight = 1.0f;
        float owidth = 0.0f;
        float oheight = 0.0f;
        if (tex.width >= tex.height)
        {
            theight = (float)tex.width / tex.height; // Tiling
            oheight = oheight = (theight -1) / -2; // Offset
        }
        else
        {
            twidth = (float)tex.height / tex.width;
            owidth = (twidth -1) / -2;
        }

        // Set texture to material albedo
        bmp.mainTexture = tex;

        // Scale object UVs to fit texture
        bmp.mainTextureScale = new Vector2(twidth, theight);
        bmp.mainTextureOffset = new Vector2(owidth, oheight);
    }

    public static Texture2D LoadPNG(string filePath)
    {
        Texture2D tex = null;
        byte[] fileData;
        if (File.Exists(filePath))
        {
            fileData = File.ReadAllBytes(filePath);
            tex = new Texture2D(2, 2, TextureFormat.ARGB32, false);
            tex.filterMode = FilterMode.Point;
            tex.wrapMode = TextureWrapMode.Clamp;
            tex.LoadImage(fileData);
        }
        return tex;
    }
}

It’s now OK for aspect ratio and centering…but as you can see I would now need to set the pixels to black when outside of UV coordinates!

Could you please tell me how to proceed (I’m really noob at shading)?
I saw on this page (http://metalbyexample.com/textures-and-samplers/) that this would be known as “Clamp-to-zero addressing”?

Thanks for your help!

PS: would all this be possible entirely through shader (tiling/offsetting/clamping)?

Shader "Custom/Aspect Fix" {
    Properties {
        _MainTex ("Image", 2D) = "white" {}
    }

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

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            sampler2D _MainTex;
            float4 _MainTex_ST;

            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            v2f vert (appdata_base v) {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.texcoord;
                return o;
            }

            half4 frag (v2f i) : SV_Target
            {
                half4 c = tex2D (_MainTex, TRANSFORM_TEX (i.uv, _MainTex));
                if (i.uv.x <= 0 || i.uv.y <= 0 || i.uv.x >= 1 || i.uv.y >= 1)
                    c = half4 (0,0,0,1);
                return c;
            }
            ENDCG
        }
    }
    Fallback "Legacy/Diffuse"
}

Something like that should work. Honestly I haven’t tried it (and wrote the whole thing out of memory inside this text editor), so there may be some issues/things I could add if you want. This shader will add a one pixel wide black border to your images, however.

Namey5,

Thank you so much for the reply and for taking time to write the code down!
Unfortunately, it doesn’t work as expected: the bitmap is stretched all the way out, manually settings tiling and offset seems ‘broken’
(btw, I don’t know if this code is to replace the tile/offset in code, so doing everything in the shader…or if I still need the code)

Whoops, forgot to add tiling controls in the shader. It should be fixed now.

Thanks Namey5…but still no black zone :wink:
(nor aspect ratio correction…but I don’t know if you intend to do this with shader too!?)

You are using old variables which generally don’t work anymore. In your script, instead of;

bmp.mainTexture = tex;
// Scale object UVs to fit texture
bmp.mainTextureScale = new Vector2(twidth, theight);
bmp.mainTextureOffset = new Vector2(owidth, oheight);

Try;

bmp.SetTexture ("_MainTex", tex);
// Scale object UVs to fit texture
bmp.SetTextureScale ("_MainTex", new Vector2 (twidth, theight));
bmp.SetTextureOffset ("_MainTex", new Vector2 (owidth, oheight));

Thanks for notifying Namey! I’v adapted the code!

…but still no black pixels outside of UV coordinates :frowning:

Again, error on my part. I wasn’t using the transformed UVs for the border check, so it was essentially doing nothing. I’ve checked this in unity and it works for me;

Shader "Custom/Aspect Fix" {
    Properties {
        _MainTex ("Image", 2D) = "white" {}
    }
    
    SubShader {
        Tags { "RenderType"="Opaque" }
    
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
    
            #include "UnityCG.cginc"
    
            sampler2D _MainTex;
            float4 _MainTex_ST;
    
            struct v2f {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
    
            v2f vert (appdata_base v) {
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.uv = TRANSFORM_TEX (v.texcoord, _MainTex);
                return o;
            }
    
            half4 frag (v2f i) : SV_Target
            {
                half4 c = tex2D (_MainTex, i.uv);
                if (i.uv.x <= 0.0f || i.uv.y <= 0.0f || i.uv.x >= 1.0f || i.uv.y >= 1.0f)
                    c = half4 (0,0,0,1);
                return c;
            }
            ENDCG
        }
    }
    Fallback "Legacy/Diffuse"
}
2 Likes

Yesss! It’s working nicely!
Thanks a LOT for the time taken to help me, Namey! Really!

Namey5,
Would you please help me know what I would have to do to display ‘transparent’ pixel instead of black ones?

To answer your question you change “half4 (0,0,0,1)” the last number is the opacity (or calling discard), but at this point you would be better off resizing the quad in the vertex shader and never running the fragment shader for those pixels

nat42,
Thanks a lot for your reply! Unfortunately, switching this opacity number to 0 didn’t change anything, black is still displayed

Waow…that look promising…but have no idea on how to achieve this…any starting help please? :wink:

Ik the post is pretty old, but is there any way to get the same functionality of the code in Shader Graph for the URP? I couldnt find anything on google. Would realy appreciate any help!:slight_smile:

2 Likes

Thank you!!!