Use two cameras. The one with the higher depth value needs to use the Depth Only clear mode, which only overwrites color where meshes it sees have materials that render color data. Make sure that the lower-depth camera doesn't render the layer that your character is on.
With this shader, we'll render into the depth buffer only, which, because it renders first (Background queue), will show whatever the lower-depth camera renders, as long as no other meshes are closer to the camera. You will need to create an RGBA texture, where the alpha channel is white where your character's body is, and black everywhere else:
Shader "Alpha Test Depth Mask" {
Properties {
_MainTex ("Texture", 2D) = ""
}
Category {
Tags {Queue = Background}
ColorMask 0
SubShader {Pass {
GLSLPROGRAM
varying lowp vec2 uv;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = gl_MultiTexCoord0.xy;
}
#endif
#ifdef FRAGMENT
uniform lowp sampler2D _MainTex;
void main() {
if (texture2D(_MainTex, uv).a < .5) discard;
}
#endif
ENDGLSL
}}
SubShader {Pass {
AlphaTest Greater 0.5
SetTexture[_MainTex]
}}
}
}
So there's the first step, creating a "hole" into the other "world". Next, you want a black outline. Black is easy, because it means we don't need any contribution of the character's texture. This is handy, because we can store the blending data in RGB, instead of A; A was already used, so the only choice is to use at least one other channel for blending data (if we want to keep this whole thing nice and compact in a single texture). Using all three is fine, and should lead to optimal compression:
Long story short, paint a black outline on a white background, in RGB (like you showed above).
Shader "Black Sprite Outline" {
Properties {
_MainTex ("Texture", 2D) = ""
}
Category {
Tags {Queue = Transparent}
ZWrite Off
Blend Zero SrcColor
SubShader {Pass {
GLSLPROGRAM
varying lowp vec2 uv;
#ifdef VERTEX
void main() {
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
uv = gl_MultiTexCoord0.xy;
}
#endif
#ifdef FRAGMENT
uniform lowp sampler2D _MainTex;
void main() {
gl_FragColor = texture2D(_MainTex, uv);
}
#endif
ENDGLSL
}}
SubShader {Pass {
SetTexture[_MainTex]
}}
}
}
It will look best if the "body" in the Alpha channel is bigger than the inner white negative space of the RGB outline. The black outline should be soft, and cover up the jagged aliased edges from the alpha test in the other shader. Unfortunately, you can't use two different queues, in a shader, so you're going to have to use two materials on the same mesh. (If we didn't put the outline in a late-queue shader, like Transparent, things would get really weird.)
I'm pretty sure there is a way, one I'd try would involve using an alpha'd PNG for the character and another texture for the background, and play with the UV's or tiling offsets, but since I have no solid answer, I'll leave that for someone else.
– DaveAI THINK that "linespacing" is the difference between font.lineHeight, and font.ascent.
– Glurth