- Home /
How to access another sprite's texture from a 2D shader
Hello!
First of all, please treat me as a complete shader noob! This is the first experience with shaders of any kind that I have had.
Although much of the jargon is lost on me, I have managed to write two simple shaders (code below) that produce a 2D lighting effect in Unity (the materials themselves are attached to 4.6 UI Image components if that matters -- I need them to be in the UI -- so no sprite renderer component allowed).
The "Light" shader takes a grayscale image and renders it on top of 2D sprites as a light source. It does this by multiplying the existing pixel color on the screen (DstColor) by the grayscale value of the texture at that pixel and then adds that value to the existing pixel color on the screen.
That grayscale texture looks like this:
The complimentary "Lit" shader (for everything that isn't a light source) simply divides all the color values by 2. That way, where the light texture color value = 1, the lit object will have its full original color (saturation?) value. And where the color value = 0, the lit object will have half that value.
My shaders work fine for the base case application.
But then I wanted to see if I could add a rim lighting effect to the sprites affected by lighting. Here are three different gifs showing my solution in action:
I did this by giving the edges of the "Lit" sprites a graded alpha value, and then in my "Light" shader I blended the inverse of the on-screen alpha value (OneMinusDstAlpha) with the same 0-1 grayscale value from the light texture in a preliminary pass. The result is that the rim lighting color only gets applied to those places on the underlying sprites that have alpha values less than 1. And the amount that gets added is a function of both the light texture value and the sprite's alpha value.
The cart and its graded alpha value:
It looks okay but the one big problem (and the reason I'm posting) is that I don't know of a way to get that alpha value from the "Lit" sprite without having the sprite actually show transparent edges in the scene. I want to be able to have a fully opaque sprite, then have a separate grayscale texture with which I can decide where to add this rim lighting.
But since I'm doing the addition of rim lighting in the "Light" shader I can't just pass in a texture to use.
So tl;dr - Is there a way to grab a texture from a material that you are drawing on top of? Or, is there a way to achieve this effect by adding the rim lighting in the "Lit" shader somehow?
Thanks in advance.
Here are the two shaders:
Shader "UI Lights/Light"
{
Properties
{
_MainTex ("Light Texture", 2D) = "white" {}
_LightColor ("Light Color", Color) = (1.0, 1.0, 1.0, 1.0)
_LightIntensity ("Light Intensity", Range(0.0, 2.0)) = 1.0
_RimColor ("Rim Lighting Color", Color) = (1.0, 1.0, 1.0, 1.0)
_RimIntensity ("Rim Intensity", Range(0.0, 2.0)) = 1.0
}
SubShader
{
pass
{
Blend OneMinusDstAlpha One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//user defined variables
uniform sampler2D _MainTex;
uniform float4 _RimColor;
uniform float _RimIntensity;
//in/out structs
struct vertexInput
{
float4 vertex : POSITION;
float2 uv_MainTex : TEXCOORD0;
};
struct vertexOutput
{
float4 vertex : POSITION;
float2 uv_MainTex : TEXCOORD0;
};
//vertex
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.uv_MainTex = v.uv_MainTex;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
//fragment
float4 frag(vertexOutput i) : COLOR
{
float4 lightValue = tex2D(_MainTex, i.uv_MainTex);
return _RimIntensity * _RimColor * lightValue;
//DstAlpha should be 1.0 on every pixel after this point as _MainTex has alpha 1.0 everywhere
}
ENDCG
}
Pass
{
Blend DstColor One
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//user defined variables
uniform sampler2D _MainTex;
uniform float4 _LightColor;
uniform float _LightIntensity;
//in/out structs
struct vertexInput
{
float4 vertex : POSITION;
float2 uv_MainTex : TEXCOORD0;
};
struct vertexOutput
{
float4 vertex : POSITION;
float2 uv_MainTex : TEXCOORD0;
};
//vertex
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.uv_MainTex = v.uv_MainTex;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
//fragment
float4 frag(vertexOutput i) : COLOR
{
float4 lightValue = tex2D(_MainTex, i.uv_MainTex);
return _LightIntensity * _LightColor * lightValue;
}
ENDCG
}
}
}
Shader "UI Lights/Lit"
{
Properties
{
_MainTex ("Sprite Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//user defined variables
uniform sampler2D _MainTex;
//in/out structs
struct vertexInput
{
float4 vertex : POSITION;
float2 uv_MainTex : TEXCOORD0;
};
struct vertexOutput
{
float4 vertex : POSITION;
float2 uv_MainTex : TEXCOORD0;
};
//vertex
vertexOutput vert(vertexInput v)
{
vertexOutput o;
o.uv_MainTex = v.uv_MainTex;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
return o;
}
//fragment
float4 frag(vertexOutput i) : COLOR
{
float4 col = tex2D(_MainTex, i.uv_MainTex);
return float4(col.rgb / 2.0, col.a);
}
ENDCG
}
}
}