Raymarching inside compute shader .

Minimal example for beginners:

//In Unity3D editor, add 3D Object/Quad to Main Camera. Set quad position at (x=0 ; y=0; z=0.86;). Add current script into quad. Set window size to 512x512 or 1024x1024.
//Put raymarching_direct_compute.compute into Assets/Resources directory. Play.
using UnityEngine;
using System.Collections;

public class raymarching_direct_compute : MonoBehaviour
{   
    void Start ()
    {
        RenderTexture render_texture = new RenderTexture(1024,1024,0);
        render_texture.enableRandomWrite = true;
        render_texture.Create();
        ComputeShader compute_shader = (ComputeShader)Resources.Load("raymarching_direct_compute");
        compute_shader.SetTexture(0,"render_texture",render_texture);
        compute_shader.Dispatch(0,render_texture.width/8,render_texture.height/8,1);
        Renderer renderer = GetComponent<Renderer>();
        renderer.material = new Material(Shader.Find("Unlit/Texture"));
        renderer.material.mainTexture = render_texture;
    }
}
#pragma kernel CSMain

RWTexture2D<float4> render_texture;

float sphere (float3 p,float3 c,float r)
{
    return length (p-c)-r;
}

float map (float3 p)
{
    return sphere (p,float3(0.0,0.0,0.0),1.0);
}

float3 set_normal (float3 p)
{
    float3 x = float3 (0.01,0.00,0.00);
    float3 y = float3 (0.00,0.01,0.00);
    float3 z = float3 (0.00,0.00,0.01);
    return normalize(float3(map(p+x)-map(p-x), map(p+y)-map(p-y), map(p+z)-map(p-z)));
}

float3 lighting ( float3 p)
{
    float3 AmbientLight = float3 (0.1,0.1,0.1);
    float3 LightDirection = normalize(float3(4.0,10.0,-10.0));
    float3 LightColor = float3 (1.0,1.0,1.0);
    float3 NormalDirection = set_normal(p);
    return  max ( dot(LightDirection, NormalDirection),0.0) * LightColor + AmbientLight;
}

float4 raymarch (float3 ro,float3 rd)
{
    for (int i=0;i<128;i++)
    {
        float t = map(ro);
        if (t<0.01) return float4(lighting(ro),1.0); else ro+=t*rd;
    }
    return float4(0.0,0.0,0.0,1.0);
}

[numthreads(8,8,1)]
void CSMain (uint2 id : SV_DispatchThreadID)
{ 
    float2 resolution = float2 (1024,1024); 
    float2 coordinates = float2 (id.x,id.y);
    float2 p = (2.0*coordinates.xy-resolution.xy)/resolution.y;
    float3 ro = float3 (0.0,0.0,-10.0);
    float3 rd = normalize( float3(p.xy,2.0));
    render_texture[id] = raymarch( ro, rd );
}

Source:

3 Likes

Any reason to use a quad rather than a blit? I’m trying to figure out how to apply this to VR and getting eye separation with quads seems hackier.

I will prepare second example, this time with Graphics.Blit.
And content maybe more advanced, like my last work:

Here is quick fix to first example.
Assign CS script to main camera. Then create material with unlit.shader and assign to script. Play.
Try to change camera position in game mode :slight_smile:

using UnityEngine;
using System.Collections;

public class raymarching_direct_compute : MonoBehaviour
{   
    public Material material;
    ComputeShader compute_shader;
    RenderTexture render_texture;
    float width,height;
   
    void Start ()
    {
        render_texture = new RenderTexture(Screen.width,Screen.height,0);
        render_texture.enableRandomWrite = true;
        render_texture.Create();
        compute_shader = (ComputeShader)Resources.Load("raymarching_direct_compute");
        compute_shader.SetTexture(0,"render_texture",render_texture);       
        material.SetTexture("MainTex",render_texture);
    }
   
    void Update()
    {
        compute_shader.SetVector("ray",Camera.main.gameObject.transform.position);
        compute_shader.SetFloat("height",Screen.height);
        compute_shader.SetFloat("width",Screen.width);       
        compute_shader.Dispatch(0,render_texture.width/8,render_texture.height/8,1);
    }
   
    void OnRenderImage (RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit (source, destination, material);
    }
   
}
#pragma kernel CSMain

RWTexture2D<float4> render_texture;

float width,height;
float4 ray;

float sphere (float3 p,float3 c,float r)
{
    return length (p-c)-r;
}

float map (float3 p)
{
    return sphere (p,float3(0.0,0.0,0.0),1.0);
}

float3 set_normal (float3 p)
{
    float3 x = float3 (0.01,0.00,0.00);
    float3 y = float3 (0.00,0.01,0.00);
    float3 z = float3 (0.00,0.00,0.01);
    return normalize(float3(map(p+x)-map(p-x), map(p+y)-map(p-y), map(p+z)-map(p-z)));
}

float3 lighting ( float3 p)
{
    float3 AmbientLight = float3 (0.1,0.1,0.1);
    float3 LightDirection = normalize(float3(4.0,10.0,-10.0));
    float3 LightColor = float3 (1.0,1.0,1.0);
    float3 NormalDirection = set_normal(p);
    return  max ( dot(LightDirection, NormalDirection),0.0) * LightColor + AmbientLight;
}

float4 raymarch (float3 ro,float3 rd)
{
    for (int i=0;i<128;i++)
    {
        float t = map(ro);
        if (t<0.01) return float4(lighting(ro),1.0); else ro+=t*rd;
    }
    return float4(0.0,0.0,0.0,1.0);
}

[numthreads(8,8,1)]
void CSMain (uint2 id : SV_DispatchThreadID)
{ 
    float2 resolution = float2 (height,width); 
    float2 coordinates = float2 (id.x,id.y);
    float2 p = (2.0*coordinates.xy-resolution.xy)/resolution.y;
    float3 rd = normalize( float3(p.xy,2.0));
    render_texture[id] = raymarch( ray.xyz, rd );
}
Shader "unlit"
{
    Properties
    {
        MainTex ("Texture", 2D) = "white" {}
    }
    Subshader
    {
        Pass
        {
            CGPROGRAM
           
            #pragma vertex vertex_shader
            #pragma fragment pixel_shader
            #pragma target 2.0

            sampler2D MainTex;
                       
            struct custom_type
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
            };
           
            custom_type vertex_shader (float4 vertex : POSITION, float2 uv : TEXCOORD0)
            {
                custom_type vs;
                vs.vertex = UnityObjectToClipPos (vertex);
                vs.uv = uv;
                return vs;
            }

            float4 pixel_shader (custom_type ps) : COLOR
            {
                return tex2D(MainTex,ps.uv.xy);
            }
            ENDCG
        }
    }
}

Przemyslaw_Zaworski how do you handle the camera rotation? Right now rotating the camera has no effects. Cheers!