Volumetric Sphere Shader

This is my implementation of Alan Zucconi’s tutorial on Volumetric Rendering.

This is a vertex and fragment shader.

An important observation I made is the order of declaring functions is important. A function must be declared before it can be called by another function.

I expanded on the tutorial by declaring Properties, making the center of the sphere and its size available from Unity’s inspector. This way I could play with values without updating the shader each time.

Properties
{
	_Center ("Center", Vector) = (0, 0, 0, 0)
	_Radius ("Radius", Float) = 0.5
}

In the vertex function I get the world position of the vertex for use in the fragment function.

v2f vert (appdata_base input)
{
	v2f output;
	output.vertex = UnityObjectToClipPos(input.vertex);
	output.wPos = mul(unity_ObjectToWorld, input.vertex).xyz; 

	return output;
}

The fragment function is where most of the work in this shader happens.

fixed4 frag (v2f input) : SV_Target
{
	float3 worldPos = input.wPos;
	float3 viewDir = normalize(input.wPos - _WorldSpaceCameraPos);

	fixed4 color;

	if (raymarchHit(worldPos, viewDir))
	{
		color = fixed4(1,0,0,1);
	}
	else
	{
		color = fixed4(1,1,1,1);
	}

	return color;
}

Full listing:

Shader "Custom/VolumetricSphere"
{
	Properties
	{
		_Center ("Center", Vector) = (0, 0, 0, 0)
		_Radius ("Radius", Float) = 0.5
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			
			float3 _Center;
			float _Radius;
		
			#define STEPS 64
			#define STEP_SIZE 0.01

			struct v2f
			{
				float4 vertex : SV_POSITION;
				float3 wPos : TEXCOORD1;
			};

			bool sphereHit(float3 pos)
			{
				return distance(pos, _Center) < _Radius;
			}

			bool raymarchHit(float3 worldPos, float3 viewDir)
			{
				for (int i = 0; i < STEPS; i++)
				{
					if (sphereHit(worldPos))
					{
						return true;
					}

					worldPos += viewDir * STEP_SIZE;
				}

				return false;
			}

			v2f vert (appdata_base input)
			{
				v2f output;
				output.vertex = UnityObjectToClipPos(input.vertex);
				output.wPos = mul(unity_ObjectToWorld, input.vertex).xyz; 

				return output;
			}

			fixed4 frag (v2f input) : SV_Target
			{
				float3 worldPos = input.wPos;
				float3 viewDir = normalize(input.wPos - _WorldSpaceCameraPos);

				fixed4 color;

				if (raymarchHit(worldPos, viewDir))
				{
					color = fixed4(1,0,0,1);
				}
				else
				{
					color = fixed4(1,1,1,1);
				}

				return color;
			}

			ENDCG
		}
	}
}
css.php