# Correct way to project a texture with alpha and colors onto surfaces in Unity

I am experimenting with Unity and wanted to project a unit circle beneath a unit and my very first naive approach was to create a child plane below the unit, assign the material to it and stretch it using the Pro Builder UV editor but it looked horrible:

In order to figure out what's wrong I read a lot of various approaches to "fix" that issue which were mostly the following:

• use a higher resolution for the texture
• do not use any compression
• do not use a POT resolution
• change the filtering option from bilinear to point
• ...

Eventually I figured that I have to do something completely different and that is how I stumbled upon the idea to use a shader attached to a projector game object in order to project a material. Unfortunately though it turns out the shader expects a grayscale image and only tints the projection based on a parameter. For reference this is the shader I have just copied from the web (source):

Shader "Projector/AdditiveTint" {
Properties {
_Color ("Tint Color", Color) = (1,1,1,1)
_Attenuation ("Falloff", Range(0.0, 1.0)) = 1.0
}
Tags {"Queue"="Transparent"}
Pass {
ZWrite Off
Blend SrcAlpha One // Additive blending
Offset -1, -1

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

struct v2f {
float4 uvShadow : TEXCOORD0;
float4 pos : SV_POSITION;
};

float4x4 unity_Projector;
float4x4 unity_ProjectorClip;

v2f vert (float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos (vertex);
o.uvShadow = mul (unity_Projector, vertex);
return o;
}

fixed4 _Color;
float _Attenuation;

fixed4 frag (v2f i) : SV_Target
{
// Apply alpha mask
fixed4 outColor = _Color * texCookie.a;
// Attenuation
float depth = i.uvShadow.z; // [-1 (near), 1 (far)]
return outColor * clamp(1.0 - abs(depth) + _Attenuation, 0.0, 1.0);
}
ENDCG
}
}
}


With that shader I was able to render the following effect:

Since I have no experience with shaders at all I just blatantly copy & pasted it and do not know (yet) how to customize them or even write custom ones in the first place.

Assuming my approach to combine a projector with such a shader is the way to go, I'd like to know whether there is any shader you can share that allows me to project an unlit texture with transparency and its own color such as the following example:

• For an effect like this, unless you're trying to conform to a bumpy ground surface, I'd say your first solution of using quad was a better choice than a projector. Projectors don't do any special magic that solves pixelation or compression, and touching up your asset & shader can give you wins as great or greater with a simple flat UV-mapped quad. May 27 '18 at 6:20
• @DMGregory Unfortunately with all suggestions as to how to fix the blurry/fuzzy texture on a plane I wasn't able to fix it at all. I tried almost everything possible from colors, compression, file format, filtering - it's just that I don't understand what is wrong since I am just starting out. And with regards to the bumpy surface: even though It won't be that bumpy in my case I'd appreaciate a solution like the current one that projects the texture on a specified layer. May 27 '18 at 10:34

After a lot of research I finally found a shader, that for the time being, does what I want to which was described in the Unity forums. The corresponding shader code is the following:

Shader "Projector/Tattoo" {
Properties {
}

Tags {
"RenderType" = "Transparent"
"Queue" = "Transparent+100"
}
Pass {
ZWrite Off
Offset -1, -1

Fog{ Mode Off }

Blend OneMinusSrcAlpha SrcAlpha

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

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

float4x4 unity_Projector;
float4 _Color;

v2f vert(appdata_tan v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = mul(unity_Projector, v.vertex);
return o;
}

half4 frag(v2f i) : COLOR
{
half4 tex = tex2Dproj(_ShadowTex, i.uv);
tex.a = 1 - tex.a;
if (i.uv.w < 0)
{
tex = float4(0,0,0,1);
}
return tex;
}
ENDCG
}
}
}


Since I don't know anything about shaders I'd appreciate if you have optimziation suggestions.

Well, for my bullet hole shader I find the issue to remove the transparent alpha effect by changing my "Blend" with your "Blend OneMinusSrcAlpha SrcAlpha".

// Upgrade NOTE: replaced '_Projector' with 'unity_Projector'
// Upgrade NOTE: replaced '_ProjectorClip' with 'unity_ProjectorClip'

Properties {
_FalloffTex ("FallOff", 2D) = "white" {}
}
Tags {"Queue"="Transparent"}
Pass {
ZWrite Off
Blend OneMinusSrcAlpha SrcAlpha
Offset -1, -1

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

struct v2f {
float4 uvShadow : TEXCOORD0;
float4 uvFalloff : TEXCOORD1;
UNITY_FOG_COORDS(2)
float4 pos : SV_POSITION;
};

float4x4 unity_Projector;
float4x4 unity_ProjectorClip;

v2f vert (float4 vertex : POSITION)
{
v2f o;
o.pos = UnityObjectToClipPos(vertex);
o.uvShadow = mul (unity_Projector, vertex);
o.uvFalloff = mul (unity_ProjectorClip, vertex);
UNITY_TRANSFER_FOG(o,o.pos);
return o;
}

sampler2D _FalloffTex;

fixed4 frag (v2f i) : SV_Target
{
texS.a = 1.0-texS.a;

fixed4 texF = tex2Dproj (_FalloffTex, UNITY_PROJ_COORD(i.uvFalloff));
fixed4 res = lerp(fixed4(1,1,1,1), texS, texF.a);

UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1));
return res;
}
ENDCG
}
}
}


The unity standard assets contains a projector game object, material, and shader. Just drag into your scene, reposition, drag material into projector, and drag image into shader “cookie” field.

• Can you change the color via code then? Would you do that by getting the material color and changing it? Feb 27 '20 at 18:17
• You would have different “cookie” textures. So you would say have a red circle for enemies and green circle for allies. Then depending on the type of unit you would swap the cookie texture. Feb 28 '20 at 6:40

The former additive shader wasn't working for me, so I made the working version. This is my first ever shader, I don't even know what this script language is called and never read any doc about shaders, but hey it's working, so feel free to grab :)

Shader "Projector/AdditiveTint" {
Properties{
_Color("Tint Color", Color) = (1,1,1,1)
}

Tags {
"RenderType" = "Transparent"
"Queue" = "Transparent+100"
}
Pass {
ZWrite Off
Offset -1, -1

//Fog{ Mode Off }

Blend One One

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

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

float4x4 unity_Projector;

v2f vert(appdata_tan v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = mul(unity_Projector, v.vertex);
return o;
}

fixed4 _Color;
half4 frag(v2f i) : COLOR
{
half4 tex = tex2Dproj(_ShadowTex, i.uv);
tex.a = 1 - tex.a;
if (i.uv.w < 0)
{
tex = float4(0,0,0,1);
}
return _Color * tex;
}
ENDCG
}
}
}

• Jul 31 '20 at 18:35