- Home /
How to render a radius?
I want to render an action radius like this spell radius from dota: https://youtu.be/vhz6vOC1vvQ
I've successfully set up a selection indicator for my units using a projector. But this works only for small circles. I've I use the same mechanism for the action radius by increasing the orthographic size the texture is scaled up and by this reason the selection circle and radius circle does not have the same thickness.
How could I realize such an effect like dota?
Thanks
Answer by Namey5 · Sep 08, 2016 at 06:51 AM
Shader "Custom/Radius Shader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_Radius ("Radius", Float) = 1
_Mid ("Midpoint", Vector) = (0,0,0,0)
_Width ("Width", Range(0,1)) = 0.1
}
SubShader {
Tags { "RenderType"="Transparent" "Queue"="Transparent" }
LOD 200
Pass {
Blend SrcAlpha OneMinusSrcAlpha
ZWrite Off
ZTest Always
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
float3 objPos : TEXCOORD0;
};
v2f vert (appdata_base v) {
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.objPos = v.vertex;
return o;
}
half _Radius;
float3 _Mid;
half _Width;
fixed4 _Color;
half4 frag (v2f i) : SV_Target
{
half circle = pow (i.objPos.x - _Mid.x, 2) + pow (i.objPos.y - _Mid.y, 2) + pow (i.objPos.z - _Mid.z, 2);
if (circle <= pow (_Radius, 2) && circle >= pow (_Radius - _Width, 2))
return _Color;
else
return half4 (0,0,0,0);
}
ENDCG
}
}
FallBack "Diffuse"
}
Attach this to a quad and it should render a radius. I'm not sure which element of the video in particular you want, but this should work.
Note It will render on top of everything else, which can be changed by the ZTesting.
Thanks for your hint. Will this work with unity terrains? I think I have read something, that I have to use projectors for this. Is there a way to use a textured circle?
Note: I'm want to realize the first seconds of the video, where you see a sorcerer, with a selection circle and another radius circle defining the spell range radius.
This shader is not really meant for an object, but more to fake the effect using a separate object. In this instance, you would create a quad with the scale and radius you want, then have it as a child of your character object at ground height. If you need it projected onto a terrain, then a projector would do the trick, although there isn't really a way to get around the issue of texture scaling. I'll see if i can convert this to a projector shader, but there is no guarantee.
// Upgrade NOTE: replaced '_Projector' with 'unity_Projector'
Shader "Projector/Radius" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_Radius ("Radius", Float) = 1
_$$anonymous$$id ("$$anonymous$$idpoint", Vector) = (0,0,0,0)
_Width ("Width", Range(0,1)) = 0.1
}
SubShader {
Pass {
Blend SrcAlpha One$$anonymous$$inusSrcAlpha
// add color of _ShadowTex to the color in the framebuffer
ZWrite Off // don't change depths
Offset -1, -1 // avoid depth fighting
CGPROGRA$$anonymous$$
#pragma vertex vert
#pragma fragment frag
// User-specified properties
half _Radius;
float3 _$$anonymous$$id;
half _Width;
uniform fixed4 _Color;
// Projector-specific uniforms
uniform float4x4 unity_Projector; // transformation matrix
// from object space to projector space
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NOR$$anonymous$$AL;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 posProj : TEXCOORD0;
// position in projector space
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.posProj = mul(unity_Projector, input.vertex);
output.pos = mul(UNITY_$$anonymous$$ATRIX_$$anonymous$$VP, input.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
if (input.posProj.w > 0.0) // in front of projector?
{
half circle = pow (input.posProj.x - _$$anonymous$$id.x, 2) + pow (input.posProj.y - _$$anonymous$$id.y, 2) + pow (input.posProj.z - _$$anonymous$$id.z, 2);
if (circle <= pow (_Radius + (_Width * 0.5), 2) && circle >= pow (_Radius - (_Width * 0.5), 2))
return _Color;
else
return float4 (0,0,0,0);
// alternatively: return tex2Dproj(
// _ShadowTex, input.posProj);
}
else // behind projector
{
return float4(0.0, 0.0, 0.0, 0.0);
}
}
ENDCG
}
}
Fallback "Projector/Light"
}
Here is the shader modified for use with a projector. To fix positioning automatically, you should also apply this script;
#pragma strict
@script ExecuteInEdit$$anonymous$$ode
function Update ()
{
var hit : RaycastHit;
if (!Physics.Raycast (transform.position, transform.forward, hit))
return;
var offset = Vector3.Distance (transform.position, hit.point);
gameObject.GetComponent.<Projector>().material.SetVector ("_$$anonymous$$id", Vector3 (offset / 2, offset / 2, offset));
}
Thanks for your new projector shader. But how I got this shader working? I've used a simple projector, with orthographic projection. Using my old tint shader with a texture, the radius circle is rendered, using your shader and the script, nothing is rendered.
I have updated and fixed the shader and script. I was accidentally working off two dimensional maths rather than three dimensional maths, and as such the shader did not work without the projector directly facing the normal. It works off raycasting however, so if you just want it to effect the terrain you can add a layermask for the ray.
Hmm. strange got your shader not working. I will check it at home, my midday break is over and from home I could post you an image if required. Thanks.
$$anonymous$$ake sure the projector is not orthographic, completely forgot. The shader should do that for you.
I've tried your shader and it works as promised. Thanks for your help!