Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 12 Next capture
2021 2022 2023
1 capture
12 Jun 22 - 12 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by TomHemery · Feb 05, 2020 at 04:23 PM · renderingshadersshader programmingdepth-buffervolumetric

Rendering volumetric objects and maintaining correct depth

So, I am fairly new to writing shaders in Unity, and I've recently been involved in making a system for visualizing volumetric data in the software. I have a simple raymarching shader that is capable of rendering volumes (stored as 3D textures) to a sufficient quality within a box geometry, but am having considerable issues in getting everything to render correctly depth wise - I am stuck at either having the volume render on top of everything, or on top of only things that are directly behind the physical box geometry.

I am looking to end up with a shader that will render the volume in such a way that you can move mesh objects through it and have them behave as you'd expect.

My current shader code is:

 Shader "Custom/VolumeShaderV2WithSplitting"
 {
     SubShader
     {
         Tags
         {
             "Queue" = "Transparent" "RenderType" = "Transparent"
         }
 
         CGINCLUDE
         #include "UnityCG.cginc"
 
         //sets how far as a proportion of max distance a ray will advance at each step
         int _SamplingQuality;
 
         //the 3D texture to be rendered
         sampler3D _MainTex;
 
         //the density of the colour values within the volume
         float _Density;
 
         //camera depth texture
         uniform sampler2D _CameraDepthTexture;
 
         struct appdata
         {
             float4 vertex : POSITION;
             float2 uv : TEXCOORD0;
         };
 
         struct v2f
         {
             float4 pos : SV_POSITION;
             float2 uv : TEXCOORD0;
             float3 localPos : TEXCOORD1;
         };
 
         //vertex shader
         v2f vert(appdata v)
         {
             v2f OUT;
             OUT.pos = UnityObjectToClipPos(v.vertex);
             OUT.localPos = v.vertex.xyz;
             OUT.uv = v.uv.xy;
             return OUT;
         }
 
         // ray/cube intersection algorithm
         struct Ray
         {
             float3 origin;
             float3 direction;
         };
 
         bool IntersectBox(Ray ray, out float entryPoint, out float exitPoint)
         {
             float3 invR = 1.0 / ray.direction;
             float3 tbot = invR * (float3(-0.5, -0.5, -0.5) - ray.origin);
             float3 ttop = invR * (float3(0.5, 0.5, 0.5) - ray.origin);
             float3 tmin = min(ttop, tbot);
             float3 tmax = max(ttop, tbot);
             float2 t = max(tmin.xx, tmin.yz);
             entryPoint = max(t.x, t.y);
             t = min(tmax.xx, tmax.yz);
             exitPoint = min(t.x, t.y);
 
             return entryPoint <= exitPoint;
         }
 
         //fragment shader (calculates the colour of a specific pixel)
         float4 frag(v2f IN) : COLOR
         {
             float4 color = float4(0,0,0,0);
             float3 localCameraPosition = UNITY_MATRIX_IT_MV[3].xyz;
 
             Ray ray;
             ray.origin = localCameraPosition;
             ray.direction = normalize(IN.localPos - localCameraPosition);
 
             float entryPoint, exitPoint;
             IntersectBox(ray, entryPoint, exitPoint);
 
             if (entryPoint < 0.0) entryPoint = 0.0;
 
             float3 rayStart = ray.origin + ray.direction * entryPoint;
             float3 rayStop = ray.origin + ray.direction * exitPoint;
 
             float dist = distance(rayStop, rayStart);
             float stepSize = dist / float(_SamplingQuality);
             float3 ds = normalize(rayStop - rayStart) * stepSize;
             float3 pos = rayStop.xyz + 0.5f;
 
             float2 uv = IN.uv;
             #if UNITY_UV_STARTS_AT_TOP
             uv.y = 1 - uv.y;
             #endif
 
             float depth = LinearEyeDepth(tex2D(_CameraDepthTexture, uv).r);
 
             for (int i = _SamplingQuality; i >= 0; --i)
             {
                 float4 mask = float4(0, 0, 0, 0); 
                 float travelled = i * ds;
                 if (travelled < depth) { //if not occluded by something in the depth buffer
                     //accumulate the colour of this point 
                     mask = tex3D(_MainTex, pos);
                     color.xyz += mask.xyz * mask.w;
                 }
                 pos -= ds;
             }
             color *= _Density / (uint)_SamplingQuality;
 
             return color;
         }
         ENDCG
 
         Pass
         {
             Blend One One
             Cull front
             ZWrite off
             ZTest Always
 
             CGPROGRAM
             #pragma target 3.0
             #pragma vertex vert
             #pragma fragment frag
             ENDCG
 
         }
     }
 }
 

This represents my best attempt at combining my understanding of volumetric rendering using raymarching (in its most basic form) and the advice on implementing depth buffer testing from this.

Unfortunately I've had no luck and I'm pretty confident that I'm just making my already poorly written shader even worse at this point.

Currently the rendered scene looks like this: A head

As you can see the volume is currently rendering "on top" of everything in the scene. The aim is to have the cubes in the scene appear as you'd expect: one in front of the volume, one within it (and should be partially visible), and one behind it.

Hopefully this question isn't too general, but I'm a bit lost.

Thanks in advance if you've any advice or pointers at all.

[Updated to remove bits from shader that were concerned with implementing a slicing plane and toggling colour channels -> this is jus the parts that are relevant... hopefully]

[Update again because I included some junk that I was trying to do to fix it, whoops]

render.png (37.8 kB)
Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
0

Answer by TomHemery · Feb 07, 2020 at 05:06 PM

So I kept working on this, and as seems to be common I've more or less fixed my own problem (posting on here was out of late in the day desperation more than anything). To be honest my shader code was a little... crap... but never mind.

The new shader I have written is:

 Shader "Custom/VolumeShaderV2WithSplitting"
 {
     SubShader
     {
         Tags
         {
             "Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"
         }
 
         Pass
         {
             Blend One One
             Cull Front
             ZWrite Off
             ZTest Always
 
             CGPROGRAM
             #include "UnityCG.cginc"
             #pragma target 3.0
             #pragma vertex vert
             #pragma fragment frag
 
             //sets how far as a proportion of max distance a ray will advance at each step
             int _SamplingQuality;
 
             //the 3D texture to be rendered
             sampler3D _MainTex;
 
             //the density of the colour values within the volume
             float _Density;
 
             //camera depth texture
             uniform sampler2D _CameraDepthTexture;
 
             struct appdata
             {
                 float4 vertex : POSITION;
                 float2 uv : TEXCOORD0;
             };
 
             struct v2f
             {
                 float4 clipPos : SV_POSITION;
                 float4 projPos : TEXCOORD0;
                 float3 localPos : TEXCOORD1;
             };
 
             //vertex shader
             v2f vert(appdata v)
             {
                 v2f o;
                 o.clipPos = UnityObjectToClipPos(v.vertex);
                 o.projPos = ComputeScreenPos(o.clipPos);
                 o.localPos = v.vertex.xyz;
                 return o;
             }
 
             // ray struct
             struct Ray
             {
                 float3 origin;
                 float3 direction;
             };
 
             //ray / cube intersection (generates entry and exit point of the ray)
             bool IntersectBox(Ray ray, out float entryPoint, out float exitPoint)
             {
                 float3 invR = 1.0 / ray.direction;
                 float3 tbot = invR * (float3(-0.5, -0.5, -0.5) - ray.origin);
                 float3 ttop = invR * (float3(0.5, 0.5, 0.5) - ray.origin);
                 float3 tmin = min(ttop, tbot);
                 float3 tmax = max(ttop, tbot);
                 float2 t = max(tmin.xx, tmin.yz);
                 entryPoint = max(t.x, t.y);
                 t = min(tmax.xx, tmax.yz);
                 exitPoint = min(t.x, t.y);
 
                 return entryPoint <= exitPoint;
             }
 
             //fragment shader (calculates the colour of a specific pixel)
             float4 frag(v2f i) : COLOR
             {
                 //calculate the position of the camera relative to this point 
                 float3 localCameraPosition = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
 
                 //create a ray starting at the (local) camera position and heading towards the (local) position of this fragment
                 Ray ray;
                 ray.origin = localCameraPosition;
                 ray.direction = normalize(i.localPos - localCameraPosition);
 
                 //calcualte the entry and exit points of the ray into the box (as distances along the ray)
                 float entryPoint, exitPoint;
                 IntersectBox(ray, entryPoint, exitPoint);
                 if (entryPoint < 0.0) entryPoint = 0.0;
 
                 //calculate the entry and exit points as vectors (rayStart, rayStop)
                 float3 rayStart = ray.origin + ray.direction * entryPoint;
                 float3 rayStop = ray.origin + ray.direction * exitPoint;
 
                 //calculate the size of step through the volume and a vector that represents that exact step along the ray
                 float stepSize = (exitPoint - entryPoint) / float(_SamplingQuality);
                 float3 step = normalize(rayStop - rayStart) * stepSize;
 
                 //set pos (the current position along the ray) to be the end of the ray (the exit point from the box)
                 float3 pos = rayStop.xyz;
 
                 //work out how far from the camera the current fragment in the depth buffer is 
                 float currDistToCamera = LinearEyeDepth(tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.projPos)).r);
 
                 //step from the back of the box forward along the ray, sampling the texture as we go
                 float4 color = float4(0, 0, 0, 0);
                 for (int stepCount = _SamplingQuality; stepCount >= 0; --stepCount)
                 {
                     //work out the distance to the camera from the current position
                     float distToCamera = length(localCameraPosition - pos);
                     if (distToCamera <= currDistToCamera)//accumulate colour if we aren't occluded
                     {
                         float4 mask = tex3D(_MainTex, pos + 0.5f);
                         color.xyz += mask.xyz * mask.w;
                     }
                     pos -= step;
                 }
                 color *= _Density / (uint)_SamplingQuality;
                 return color;
             }
 
             ENDCG
             
         }
     }
 }

I even did my best to comment it properly.

The resulting output is now: alt text

As you can see it works more or less as expected, it's not ailiased unfortunately but I have a sneaking suspiscion that that's easier said than done? (If anyone knows any better I'd love to hear). But anyway in case anyone else goes into crisis like me, here is my best attempt at an answer.


render.png (40.0 kB)
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

150 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Material with custom shader black on Android (Lightweight Render Pipeline) 0 Answers

Manual shaders in URP/HDRP 1 Answer

How to write an additive HDRP shader without glow 0 Answers

How can I add volume/depth to shadows? 0 Answers

Any news about how to make use of GL_OES_EGL_image_external? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges