- Home /
World Space canvas on top of "everything" ?
Is it possible to render a world space canvas on top of everything? Example, health bars on top of all 3D objects.
Note that the trivial and usual solution here is very simply to click to add another camera and just use that camera for the "world - on top" UI elements. (Eg, debeani answer below.)
Answer by debeani · Jan 28, 2015 at 06:34 AM
If you have a lot of UI elements visually attached to game elements then you are going to want to use world space. But if you want your UI to look like an overlay. You're going to want to render your game and the UI separately and then composite them together.
Simple explanation: click to add a new camera, set depth to a high number. Any world space Canvas items (eg, health bars), simply click to use that camera; also set "order in layer" to a high number. That's it.
In detail, using a layer so you can do them "all at once":
Put your UI on a Layer called UI (if it isn't already).
Duplicate your Main Camera and call it UI Camera.
Parent your UI Camera to the Main Camera.
Remove scripts like camera control, post effects and audio listeners from the UI Camera.
In the Main Camera Culling Mask turn off UI
In the UI Camera turn everything off but UI.
In the UI Camera change Clear Flags to Depth only
In the UI Camera change the Depth to something higher than the value on your Main Camera.
Then in your Canvas set the Event Camera to the UI Camera.
This image is of the Scene view
While this is what it looks like in game (please ignore the test graphics)
For anyone this problem with a single camera in the scene, I recommend it. If you have more than one camera, it's worthy checking out DanSuperGP's solution.
Don't forget to change rendering path on UI camera to "Vertex Lit". Otherwise the strange behavior may occur
Old but gold. Thank you kind sir, this was exactly what I would never have been able to do without you. I tip my fedora, for you are indeed a gentlemen. Thank you!
Answer by DanSuperGP · Jan 27, 2015 at 11:11 PM
You're going to need a custom shader for your World Space UI objects that has it's Render Order to Overlay and has ZTest turned off.
This is a copy of the Default UI shader with the necessary changes. Should do the trick. Just make a material with this shader, and apply it to everything you want drawn over the top of geometry in your WorldSpace UI.
Shader "UI/Default_OverlayNoZTest"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
}
SubShader
{
Tags
{
"Queue"="Overlay"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
};
fixed4 _Color;
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_MATRIX_MVP, IN.vertex);
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _MainTex;
fixed4 frag(v2f IN) : SV_Target
{
half4 color = tex2D(_MainTex, IN.texcoord) * IN.color;
clip (color.a - 0.01);
return color;
}
ENDCG
}
}
}
Thank you so much! I was beggining to strugle with shaders to get just that done. It solved my own question of the same thing in http://answers.unity3d.com/questions/908364/applying-materials-to-the-new-ui-elements-46.html
With this shader how to render UI Text color. All text in the canvas ui are black.
Shader works, but I also have the problem where the text always renders black. Anyone knows how to change that?
To fix the font color rendering black modify the shader with the below changes. I've added comments to the lines that I have changed ( Done in Unity 5.2.1f1 )
Shader:
Shader "UI/Default_OverlayNoZTest"
{
Properties
{
[PerRendererData] _$$anonymous$$ainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWrite$$anonymous$$ask ("Stencil Write $$anonymous$$ask", Float) = 255
_StencilRead$$anonymous$$ask ("Stencil Read $$anonymous$$ask", Float) = 255
_Color$$anonymous$$ask ("Color $$anonymous$$ask", Float) = 15
}
SubShader
{
Tags
{
"Queue"="Overlay"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
Read$$anonymous$$ask [_StencilRead$$anonymous$$ask]
Write$$anonymous$$ask [_StencilWrite$$anonymous$$ask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest Off
Blend SrcAlpha One$$anonymous$$inusSrcAlpha
Color$$anonymous$$ask [_Color$$anonymous$$ask]
Pass
{
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
half2 texcoord : TEXCOORD0;
};
fixed4 _Color;
fixed4 _TextureSampleAdd; //Added for font color support
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, IN.vertex);
OUT.texcoord = IN.texcoord;
#ifdef UNITY_HALF_TEXEL_OFFSET
OUT.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
OUT.color = IN.color * _Color;
return OUT;
}
sampler2D _$$anonymous$$ainTex;
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_$$anonymous$$ainTex, IN.texcoord) + _TextureSampleAdd) * IN.color; //Added for font color support
clip (color.a - 0.01);
return color;
}
ENDCG
}
}
}
I tried this, since I have a similar issue where I need to have my text in world space but rendered in front of all objects in the scene and although there is a tint option in the shader, it still comes out black regardless of the colour in the tint box. I guess black tinted with any colour is still black? Anyone know how to get the colour part to work?
It does render on top though, so that works perfectly... so close but... :D
And does anyone also know how to proceed if you need a background for your text? Putting the same shader on both, they seem to render on top of each other without control on which one is in front of the other. Thanks!
awesome shader
solves my problem of 2d images which goes inside the 3d globe. But now i am facing dragging issue on 3d globe. When i apply this shader its z order changes to top but restricts mouse interation on globe.
can someone guide me what should i need to do ?
Answer by Julien-Lynge · Oct 30, 2017 at 09:53 PM
Other answers involve copying Unity's built-in UI shader and then overriding the ZTest value. Because Unity added a property for setting ZTest to the UI shaders, you can actually solve this with just a line of code. That way, you can always use Unity's latest shaders without having to hard code anything.
Here's an example script showing how to override the ZTest value so your UI always renders on top. Attach it to your canvas and it should update the UI elements automatically.
In a nutshell, I'm just making a copy of Unity's default rendering material, then changing the value of ZTest on the copy, then applying it back to the graphic. This should work for any UI graphic - text, etc. (Note: this doesn't work for TextMeshPro specifically - they are missing the keyword needed, and instead have a separate shader called 'Overlay' that does the same thing.)
[ExecuteInEditMode] //Disable if you don't care about previewing outside of play mode
public class WorldSpaceOverlayUI : MonoBehaviour
{
private const string shaderTestMode = "unity_GUIZTestMode"; //The magic property we need to set
[SerializeField] UnityEngine.Rendering.CompareFunction desiredUIComparison = UnityEngine.Rendering.CompareFunction.Always; //If you want to try out other effects
[Tooltip("Set to blank to automatically populate from the child UI elements")]
[SerializeField] Graphic[] uiElementsToApplyTo;
//Allows us to reuse materials
private Dictionary<Material, Material> materialMappings = new Dictionary<Material, Material>();
protected virtual void Start()
{
if (uiElementsToApplyTo.Length == 0)
{
uiElementsToApplyTo = gameObject.GetComponentsInChildren<Graphic>();
}
foreach (var graphic in uiElementsToApplyTo)
{
Material material = graphic.materialForRendering;
if (material == null)
{
Debug.LogError($"{nameof(WorldSpaceOverlayUI)}: skipping target without material {graphic.name}.{graphic.GetType().Name}");
continue;
}
if (!materialMappings.TryGetValue(material, out Material materialCopy))
{
materialCopy = new Material(material);
materialMappings.Add(material, materialCopy);
}
materialCopy.SetInt(shaderTestMode, (int)desiredUIComparison);
graphic.material = materialCopy;
}
}
}
This is the real solution. The other solutions change the color of the sprite. Thank you very much!!
Change Image to Graphic and you can also apply to text.
Answer by art_craft_vlad · Nov 11, 2015 at 09:00 PM
Ok, try this one: [ui-defaultfontdrawontop.zip][1] The shader from DanSuperGP had a bug in it which was messing up the colors. This is a copy of UI/Default Font shader which only needed one thing fixed: ZTest set to 'off'.
. . . . . . . .
EDIT: for some reason ZIPs are forbidden. Here is the complete text:
Shader "UI/Default Font Draw On Top" {
Properties {
_MainTex ("Font Texture", 2D) = "white" {}
_Color ("Text Color", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Lighting Off
Cull Off
ZTest Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
uniform float4 _MainTex_ST;
uniform fixed4 _Color;
v2f vert (appdata_t v)
{
v2f o;
o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
o.color = v.color * _Color;
o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
#ifdef UNITY_HALF_TEXEL_OFFSET
o.vertex.xy += (_ScreenParams.zw-1.0)*float2(-1,1);
#endif
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = i.color;
col.a *= tex2D(_MainTex, i.texcoord).a;
clip (col.a - 0.01);
return col;
}
ENDCG
}
}
}
Thanks art_craft_vlad, but I can't download the file. I get an error 'access forbidden' from that link. $$anonymous$$aybe zip files are not allowed?
This is perfect. Thank you! Now, is there a way to do the same thing for the Default UI shader rather than the Default UI Font shader? I'm asking because even though I can use this shader for all of the other UI controls like panels, images, etc, I wasn't sure if there was extra processing taking place when I know there isn't a font being utilized.
Thanks again! - jeff
Looks great, but how do you actually apply this to a canvas (or canvas element)? I tried making a material and passing it to CanvasRenderer.Set$$anonymous$$aterial, but that throws an out-of-bounds error; the docs say to see CanvasRenderer.Set$$anonymous$$aterialCount, but that, according to the same docs, does not actually exist. What am I missing?
Answer by SnStarr · Jan 19, 2015 at 11:42 AM
Sometimes you need the canvas to be in world space. For example, your character is moving around and there is a thermometer/etc which "moves with the character", turning and so on.
If you DO NOT need the UI to be in world space, the answer is triviai:
Setting the Canvas to Render itself as an Overlay on the Camera and not in World Space.
(If you need the UI in world space, this is irrelevant.)
I don't have any experience in masking layers, but how would I go about that solution? If I set the canvas to render itself as an Overlay, I can't simply add canvas objects to game objects to have the UI appear in right places. And I did try to do a manual projection before and for some reason, it resulted in a jittery motion of the UI elements on top of my in-game objects.
How smart are the canvas about how they render stuff outside the screen? I'm guessing quite smart.
I feel that having the world space UI rendering canvas will fall of short in many practical applications if there's no other way around this.
Just stumbled on this myself and this is the easiest solution I$$anonymous$$O. Once you attach a camera for the Render $$anonymous$$ode of "Screen Space - Camera", you can use the Plane Distance variable to move the gui
Your answer
![](https://koobas.hobune.stream/wayback/20220613180658im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
Text mesh not showing on a world space canvas attached to a enemy gameobject, 2D 0 Answers
How to detect if a gameobject collides with a image on a worldspace canvas 2 Answers
Convert Canvas space to World coordinates 2 Answers
How to pop up a world canvas close to an object and in front of the camera view? 1 Answer
UI canvas attached to vive controller disappears after first run in editor on vive 0 Answers