Search Unity

Question How render 3D render texture in custom shader

Discussion in 'General Graphics' started by RendergonPolygons, Mar 2, 2022.

  1. RendergonPolygons

    RendergonPolygons

    Joined:
    Oct 9, 2019
    Posts:
    98
    Hi is it possible to render a 3d render texture on a custom shader raymarching like if it was a 3dtexture?

    I use a 3D render texture because I calculate and set the color of the 3D volume in a compute shader. I set the rendertexture 3D as shown below:

    Code (CSharp):
    1. output3DRenderTexture= new RenderTexture(m_CubeDim.x, m_CubeDim.y, 0, thisTexFormat);
    2.         outpuoutput3DRenderTextureRendTex.enableRandomWrite = true;
    3.         output3DRenderTexture.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
    4.         output3DRenderTexture.volumeDepth = m_CubeDim.z;
    5.         output3DRenderTexture.Create();
    I populate the 3D RenderTexture data in a a compute shader and GetData helps me confirm the 3d render texture has all the correct color data.

    I can successfully render if I replace the 3D renderTex in the custom shader's sampler3D for a 3DTexture I create with the Tex2D slices.
    Code (CSharp):
    1. cubeRenderer.material.SetTexture("_MainTex", output3DRenderTexture);//this does not render
    versus
    Code (CSharp):
    1. cubeRenderer.material.SetTexture("_MainTex", outputTexture3D);//this renders
    This post in 2016 seems to suggest it's possible to render 3d render textures in custom shaders but it may now be outdated, it doesn't work for me and no error shows either.

    It seems to me there maybe a significant performance hit if create the Textures2D slices in GPU, carry on with the creation of the Texture3D on CPU and re-send this Tex3D to GPU for the custom shader to consume it. After all the 3D volume already existed in GPU except as RenderTexture set as Tex3D. Thank you!

    Shader:

    Code (CSharp):
    1. #include "UnityCG.cginc"
    2. #define ITERATIONS 100
    3. #define PI2 6.28318530718
    4. half4 _Color;
    5. sampler3D _MainTex;
    6. half _Intensity, _Threshold;
    7. half3 _SliceMin, _SliceMax;
    8. float4x4 _AxisRotationMatrix;
    9. float _Angle;
    10. struct Ray {
    11.     float3 origin;
    12.     float3 dir;
    13. };
    14. struct AABB {
    15.     float3 min;
    16.     float3 max;
    17. };
    18. // https http.download.nvidia.com/developer/presentations/2005/GDC/Audio_and_Slides/VolumeRendering_files/GDC_2_files/GDC_2005_VolumeRenderingForGames_files/Slide0073.htm
    19. bool intersect(Ray r, AABB aabb, out float t0, out float t1)
    20. {
    21.     float3 invR = 1.0 / r.dir;
    22.     float3 tbot = invR * (aabb.min - r.origin);
    23.     float3 ttop = invR * (aabb.max - r.origin);
    24.     float3 tmin = min(ttop, tbot);
    25.     float3 tmax = max(ttop, tbot);
    26.     float2 t = max(tmin.xx, tmin.yz);
    27.     t0 = max(t.x, t.y);
    28.     t = min(tmax.xx, tmax.yz);
    29.     t1 = min(t.x, t.y);
    30.     return t0 <= t1;
    31. }
    32. float3 get_uv(float3 p) {
    33.     return (p + 0.5);
    34. }
    35.  
    36. float sample_volume(float3 uv, float3 p)
    37. {
    38.     float v = tex3D(_MainTex, uv).r * _Intensity;
    39.     return v;
    40. }
    41. struct appdata
    42. {
    43.     float4 vertex : POSITION;
    44.     float2 uv : TEXCOORD0;
    45. };
    46. struct v2f
    47. {
    48.     float4 vertex : SV_POSITION;
    49.     float2 uv : TEXCOORD0;
    50.     float3 world : TEXCOORD1;
    51.     float3 local : TEXCOORD2;
    52. };
    53. v2f vert(appdata v)
    54. {
    55.     v2f o;
    56.     o.vertex = UnityObjectToClipPos(v.vertex);
    57.     o.uv = v.uv;
    58.     o.world = mul(unity_ObjectToWorld, v.vertex).xyz;
    59.     o.local = v.vertex.xyz;
    60.     return o;
    61. }
    62. fixed4 frag(v2f i) : SV_Target
    63. {
    64.   Ray ray;
    65.     ray.origin = i.local;
    66.     // world space direction to object space
    67.     float3 dir = (i.world - _WorldSpaceCameraPos);
    68.     ray.dir = normalize(mul(unity_WorldToObject, dir));
    69.     AABB aabb;
    70.     aabb.min = float3(-0.5, -0.5, -0.5);
    71.     aabb.max = float3(0.5, 0.5, 0.5);
    72.     float tnear;
    73.     float tfar;
    74.     intersect(ray, aabb, tnear, tfar);
    75.     tnear = max(0.0, tnear);
    76.     // float3 start = ray.origin + ray.dir * tnear;
    77.     float3 start = ray.origin;
    78.     float3 end = ray.origin + ray.dir * tfar;
    79.     float dist = abs(tfar - tnear);
    80.     float step_size = dist / float(ITERATIONS);
    81.     float3 ds = normalize(end - start) * step_size;
    82.     float4 dst = float4(0, 0, 0, 0);
    83.     float3 p = start;
    84.     [unroll]
    85.     for (int iter = 0; iter < ITERATIONS; iter++)
    86.     {
    87.       float3 uv = get_uv(p);
    88.       float v = sample_volume(uv, p);
    89.       float4 src = float4(v, v, v, v);
    90.       src.a *= 0.5;
    91.       src.rgb *= src.a;
    92.       // blend
    93.       dst = (1.0 - dst.a) * src + dst;
    94.       p += ds;
    95.       if (dst.a > _Threshold) break;
    96. }
    97. return saturate(dst) * _Color;
    98. }
    99. #endif
     
    Last edited: Mar 3, 2022
    Sam3million likes this.
  2. Sam3million

    Sam3million

    Joined:
    Mar 21, 2018
    Posts:
    4
    Have you found an answer to this? I am having the same problem.
     
  3. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    726
    You could try a compute shader. Will probably be more capable for what you are wanting to do anyway.
     
  4. scrawk

    scrawk

    Joined:
    Nov 22, 2012
    Posts:
    804
    Got it to work.

    Attach this script to default cube at origin.

    Code (CSharp):
    1. using System.IO;
    2. using UnityEngine;
    3. using UnityEngine.Experimental.Rendering;
    4. using UnityEngine.Rendering;
    5.  
    6. using Common.Unity.Utility;
    7.  
    8. public class RayMarch : MonoBehaviour
    9. {
    10.  
    11.     private RenderTexture tex;
    12.  
    13.     public ComputeShader write;
    14.  
    15.     private void Start()
    16.     {
    17.         //use the write shader and the CBWrite utility posted in this thread.
    18.         //https://forum.unity.com/threads/blit-copytexture-readpixels-loadrawtexturedata-confusion.1241251/#post-7904548
    19.  
    20.         ///Create the render texture
    21.         var size = 32;
    22.         int channels = 1;
    23.         var format = RenderTextureFormat.RFloat;
    24.  
    25.         tex = new RenderTexture(size, size, 0, format);
    26.         tex.enableRandomWrite = true;
    27.         tex.dimension = TextureDimension.Tex3D;
    28.         tex.volumeDepth = size;
    29.         tex.Create();
    30.  
    31.         //Set the texures data.
    32.         var data = GetData(size);
    33.         var buffer = new ComputeBuffer(data.Length, sizeof(float));
    34.         buffer.SetData(data);
    35.  
    36.         //write data into texture
    37.         CBWrite.IntoRenderTexture(tex, channels, buffer, write);
    38.         buffer.Dispose();
    39.  
    40.         //set texture to material.
    41.         var renderer = GetComponent<Renderer>();
    42.         renderer.material.SetTexture("_MainTex", tex);
    43.  
    44.     }
    45.  
    46.     /// <summary>
    47.     /// The sdf of a sphere positioned at origin with a raduis of 1.
    48.     /// </summary>
    49.     /// <param name="p">A point in space.</param>
    50.     /// <returns>The sdf to the point.</returns>
    51.     public float SignedDistance(Vector3 p)
    52.     {
    53.         p = p - Vector3.zero;
    54.         return p.magnitude - 1.0f;
    55.     }
    56.  
    57.     /// <summary>
    58.     /// Create a sdf of a sphere just for something to render.
    59.     /// </summary>
    60.     /// <param name="size"></param>
    61.     /// <returns></returns>
    62.     private float[] GetData(int size)
    63.     {
    64.         var data = new float[size * size * size];
    65.  
    66.         for(int x = 0; x < size; x++)
    67.         {
    68.             for (int y = 0; y < size; y++)
    69.             {
    70.                 for (int z = 0; z < size; z++)
    71.                 {
    72.                     //create a point.
    73.                     var p = new Vector3(x, y, z) / size;
    74.                     p = (p - new Vector3(0.5f, 0.5f, 0.5f)) * 1.9f;
    75.  
    76.                     //Get the sdf and flip.
    77.                     var sdf = SignedDistance(p) * -1.0F;
    78.  
    79.                     //if point outside of sphere change alpa to 0.
    80.                     if (sdf < 0) sdf = 0;
    81.  
    82.                     data[x + y * size + z * size * size] = sdf;
    83.                 }
    84.             }
    85.  
    86.         }
    87.  
    88.         return data;
    89.     }
    90.  
    91. }
    Then use this shader

    Code (CSharp):
    1. Shader "Unlit/RayMarchSdr"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 3D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags {"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}
    10.         LOD 100
    11.  
    12.         ZWrite Off
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.  
    15.         Pass
    16.         {
    17.             CGPROGRAM
    18.             #pragma vertex vert
    19.             #pragma fragment frag
    20.             #include "UnityCG.cginc"
    21.  
    22.             #define ITERATIONS 100
    23.             #define PI2 6.28318530718
    24.  
    25.             struct appdata
    26.             {
    27.                 float4 vertex : POSITION;
    28.                 float2 uv : TEXCOORD0;
    29.             };
    30.  
    31.             struct v2f
    32.             {
    33.                 float4 vertex : SV_POSITION;
    34.                 float2 uv : TEXCOORD0;
    35.                 float3 world : TEXCOORD1;
    36.                 float3 local : TEXCOORD2;
    37.             };
    38.  
    39.             sampler3D _MainTex;
    40.  
    41.             struct Ray
    42.             {
    43.                 float3 origin;
    44.                 float3 dir;
    45.             };
    46.  
    47.             struct AABB
    48.             {
    49.                 float3 min;
    50.                 float3 max;
    51.             };
    52.        
    53.             bool intersect(Ray r, AABB aabb, out float t0, out float t1)
    54.             {
    55.                 float3 invR = 1.0 / r.dir;
    56.                 float3 tbot = invR * (aabb.min - r.origin);
    57.                 float3 ttop = invR * (aabb.max - r.origin);
    58.                 float3 tmin = min(ttop, tbot);
    59.                 float3 tmax = max(ttop, tbot);
    60.                 float2 t = max(tmin.xx, tmin.yz);
    61.                 t0 = max(t.x, t.y);
    62.                 t = min(tmax.xx, tmax.yz);
    63.                 t1 = min(t.x, t.y);
    64.  
    65.                 return t0 <= t1;
    66.             }
    67.  
    68.             float3 get_uv(float3 p)
    69.             {
    70.                 return (p + 0.5);
    71.             }
    72.  
    73.             float sample_volume(float3 uv, float3 p)
    74.             {
    75.                 float v = tex3D(_MainTex, uv).r;
    76.                 return v;
    77.             }
    78.  
    79.             v2f vert (appdata v)
    80.             {
    81.                 v2f o;
    82.                 o.vertex = UnityObjectToClipPos(v.vertex);
    83.                 o.uv = v.uv;
    84.                 o.world = mul(unity_ObjectToWorld, v.vertex).xyz;
    85.                 o.local = v.vertex.xyz;
    86.                 return o;
    87.             }
    88.  
    89.             fixed4 frag (v2f i) : SV_Target
    90.             {
    91.  
    92.                 Ray ray;
    93.                 ray.origin = i.local;
    94.                 // world space direction to object space
    95.                 float3 dir = (i.world - _WorldSpaceCameraPos);
    96.                 ray.dir = normalize(mul(unity_WorldToObject, dir));
    97.  
    98.                 AABB aabb;
    99.                 aabb.min = float3(-0.5, -0.5, -0.5);
    100.                 aabb.max = float3(0.5, 0.5, 0.5);
    101.  
    102.                 float tnear;
    103.                 float tfar;
    104.                 intersect(ray, aabb, tnear, tfar);
    105.                 tnear = max(0.0, tnear);
    106.  
    107.                 // float3 start = ray.origin + ray.dir * tnear;
    108.                 float3 start = ray.origin;
    109.                 float3 end = ray.origin + ray.dir * tfar;
    110.                 float dist = abs(tfar - tnear);
    111.                 float step_size = dist / float(ITERATIONS);
    112.                 float3 ds = normalize(end - start) * step_size;
    113.                 float4 dst = float4(0, 0, 0, 0);
    114.                 float3 p = start;
    115.  
    116.                 [unroll]
    117.                 for (int iter = 0; iter < ITERATIONS; iter++)
    118.                 {
    119.                     float3 uv = get_uv(p);
    120.  
    121.                     float v = sample_volume(uv, p);
    122.                     float4 src = float4(v,v,v,v);
    123.  
    124.                     src.a *= 0.5;
    125.                     src.rgb *= src.a;
    126.  
    127.                     // blend
    128.                     dst = (1.0 - dst.a) * src + dst;
    129.                     p += ds;
    130.  
    131.                     //if (dst.a > 0.5)
    132.                     //{
    133.                     ////    break;
    134.                     //}
    135.                 }
    136.  
    137.                 return saturate(dst) * float4(1,0,0,1);
    138.             }
    139.             ENDCG
    140.         }
    141.     }
    142. }
    143.  
    and should look something like this.

    SphereSDF.png
     
    RendergonPolygons and Torbach78 like this.
  5. RendergonPolygons

    RendergonPolygons

    Joined:
    Oct 9, 2019
    Posts:
    98
    Thanks a lot @scrawk , your code really helped me find the culprit. In my code I had to set the 3drendertext after the computeshader finalized the job.
    Code (CSharp):
    1. cubeRenderer.material.SetTexture("_MainTex", output3DRenderTexture);
    I get significantly higher fps when using 3dtex than when using 3drendtex. As per my original post above I find this surprising cos unlike the process used for a 3dtex creation, the 3d rendertex already exists in the gpu. I use 200 textures for the volume, only tested on editor. Does anyone also see this performance?

    Thanks again for your help !
     
    Last edited: Mar 9, 2022