Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Last active February 11, 2022 08:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unitycoder/7120e492deba748dba3d4e1e424d3c2a to your computer and use it in GitHub Desktop.
Save unitycoder/7120e492deba748dba3d4e1e424d3c2a to your computer and use it in GitHub Desktop.
Unity Ray Marching with Sphere Tracing
// unity ray marching with sphere marching : https://unitycoder.com/blog/2019/05/06/ray-marching/
// based on Ray Marching tutorial from The Coding Train https://www.youtube.com/watch?v=-6iIc6-Y-kk
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RayMarchingSpheres : MonoBehaviour
{
// place spheres under this root gameobject
public Transform sphereRoot;
// how many raymarch steps
public int rayMarchSteps = 10;
// list of step spheres
List<Sphere> rayPoints = new List<Sphere>();
bool isSearching = true;
void Update()
{
if (isSearching == false) return;
isSearching = false;
// build ray to mouse (2D)
var origin = Vector3.zero;
var mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
mousePos.z = 0;
var dir = (mousePos - origin).normalized;
var ray = new Ray(origin, dir);
// start from origin
var currentPos = ray.origin;
rayPoints.Clear();
float totalDistance = 0;
// raymarching steps
for (int i = 0; i < rayMarchSteps; i++)
{
float smallestDistance = Mathf.Infinity;
// find closest distance from currentPos sphere from spheres
foreach (Transform t in sphereRoot)
{
var sphere = new Sphere();
sphere.pos = t.position;
// NOTE giving fixed radius, using default unity sphere
sphere.r = 1f / 2f;
var d = SignedDistance(currentPos, sphere);
if (d < smallestDistance) smallestDistance = d;
}
// hit close enough, then stop marching
if (smallestDistance < 0.001f)
{
isSearching = false;
break;
}
// keep track of total distance
totalDistance += smallestDistance;
// record existing circle
var s = new Sphere();
s.pos = currentPos;
s.r = 1f / 2f;
s.minDist = smallestDistance;
rayPoints.Add(s);
// get next point position
var newPos = ray.GetPoint(totalDistance);
Debug.DrawLine(currentPos, newPos, rayPoints.Count % 2 == 0 ? Color.cyan : Color.yellow);
currentPos = newPos;
// draw cross at new currentpost
var c = rayPoints.Count % 2 == 0 ? Color.red : Color.blue;
Debug.DrawRay(currentPos, Vector3.up * 0.1f, c);
Debug.DrawRay(currentPos, -Vector3.up * 0.1f, c);
Debug.DrawRay(currentPos, Vector3.right * 0.1f, c);
Debug.DrawRay(currentPos, -Vector3.right * 0.1f, c);
}
}
// draw spheres
public void OnDrawGizmos()
{
Gizmos.color = Color.green;
for (int i = 0, length = rayPoints.Count; i < length; i++)
{
Gizmos.DrawWireSphere(rayPoints[i].pos, rayPoints[i].minDist);
}
isSearching = true;
}
// signed distance to sphere
float SignedDistance(Vector3 startPos, Sphere sphere)
{
var d = Vector3.Distance(startPos, sphere.pos);
return d - sphere.r;
}
}
using UnityEngine;
public struct Sphere
{
public Vector3 pos;
public float r;
public float minDist;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment