diff --git a/Assets/Editor/VolumeRenderedObjectCustomInspector.cs b/Assets/Editor/VolumeRenderedObjectCustomInspector.cs index 92f83dfa..ca85cab6 100644 --- a/Assets/Editor/VolumeRenderedObjectCustomInspector.cs +++ b/Assets/Editor/VolumeRenderedObjectCustomInspector.cs @@ -61,6 +61,19 @@ public override void OnInspectorGUI() EditorGUILayout.MinMaxSlider("Visible value range", ref visibilityWindow.x, ref visibilityWindow.y, 0.0f, 1.0f); volrendObj.SetVisibilityWindow(visibilityWindow); + if (newRenderMode == RenderMode.IsosurfaceRendering) + { + float oldThreshold = volrendObj.GetGradientVisibilityThreshold(); + float oldThresholdSqrt = Mathf.Sqrt(oldThreshold); // Convert to square root scaling (=> more precision close to 0) + float newThreshold = EditorGUILayout.Slider( + new GUIContent("Gradient visibility threshold", "Minimum gradient maginitude value that will be visible"), + oldThresholdSqrt, 0.0f, 1.0f + ); + newThreshold = newThreshold * newThreshold; // Convert back to linear scaling + if (newThreshold != oldThreshold) + volrendObj.SetGradientVisibilityThreshold(newThreshold); + } + // Transfer function settings EditorGUILayout.Space(); tfSettings = EditorGUILayout.Foldout(tfSettings, "Transfer function"); @@ -101,6 +114,19 @@ public override void OnInspectorGUI() LightSource newLightSource = (LightSource)EditorGUILayout.EnumPopup("Light source", oldLightSource); if (newLightSource != oldLightSource) volrendObj.SetLightSource(newLightSource); + + // Gradient lighting threshold: Threshold for how low gradients can contribute to lighting. + Vector2 gradLightThreshold = volrendObj.GetGradientLightingThreshold(); + // Convert to square root scaling (=> more precision close to 0) + gradLightThreshold = new Vector2(Mathf.Sqrt(gradLightThreshold.x), Mathf.Sqrt(gradLightThreshold.y)); + EditorGUILayout.MinMaxSlider( + new GUIContent("Gradient lighting threshold", + "Minimum and maximum threshold for gradient contribution to lighting.\n" + + "Voxels with gradient less than min will be unlit, and with gradient >= max will fully shaded."), + ref gradLightThreshold.x, ref gradLightThreshold.y, 0.0f, 1.0f + ); + // Convert back to linear scale, before setting updated value. + volrendObj.SetGradientLightingThreshold(new Vector2(gradLightThreshold.x * gradLightThreshold.x, gradLightThreshold.y * gradLightThreshold.y)); } } diff --git a/Assets/Scripts/VolumeObject/VolumeRenderedObject.cs b/Assets/Scripts/VolumeObject/VolumeRenderedObject.cs index c7ac5239..52f5cff2 100644 --- a/Assets/Scripts/VolumeObject/VolumeRenderedObject.cs +++ b/Assets/Scripts/VolumeObject/VolumeRenderedObject.cs @@ -1,6 +1,5 @@ using System.Threading; using System.Threading.Tasks; -using UnityEditor; using UnityEngine; namespace UnityVolumeRendering @@ -25,17 +24,33 @@ public class VolumeRenderedObject : MonoBehaviour [SerializeField, HideInInspector] private RenderMode renderMode; + [SerializeField, HideInInspector] private TFRenderMode tfRenderMode; + [SerializeField, HideInInspector] private bool lightingEnabled; + [SerializeField, HideInInspector] private LightSource lightSource; + // Minimum and maximum gradient threshold for lighting contribution. Values below min will be unlit, and between min and max will be partly shaded. + [SerializeField, HideInInspector] + private Vector2 gradientLightingThreshold = new Vector2(0.02f, 0.15f); + + // Gradient magnitude threshold. Voxels with gradient magnitude less than this will not be rendered in isosurface rendering mode. + [SerializeField, HideInInspector] + private float minGradient = 0.01f; + + // Minimum/maximum data value threshold for rendering. Values outside of this range will not be rendered. [SerializeField, HideInInspector] private Vector2 visibilityWindow = new Vector2(0.0f, 1.0f); + + // Early ray termination [SerializeField, HideInInspector] private bool rayTerminationEnabled = true; + + // Tri-cubic interpolation of data texture (expensive, but looks better) [SerializeField, HideInInspector] private bool cubicInterpolationEnabled = false; @@ -150,8 +165,39 @@ public async Task SetLightingEnabledAsync(bool enable, IProgressHandler progress public void SetLightSource(LightSource source) { - lightSource = source; - UpdateMaterialProperties(); + if (lightSource != source) + { + lightSource = source; + UpdateMaterialProperties(); + } + } + + public void SetGradientLightingThreshold(Vector2 threshold) + { + if (gradientLightingThreshold != threshold) + { + gradientLightingThreshold = threshold; + UpdateMaterialProperties(); + } + } + + public Vector2 GetGradientLightingThreshold() + { + return gradientLightingThreshold; + } + + public void SetGradientVisibilityThreshold(float min) + { + if (minGradient != min) + { + minGradient = min; + UpdateMaterialProperties(); + } + } + + public float GetGradientVisibilityThreshold() + { + return minGradient; } public void SetVisibilityWindow(float min, float max) @@ -307,6 +353,9 @@ private void UpdateMatInternal() meshRenderer.sharedMaterial.SetFloat("_MinVal", visibilityWindow.x); meshRenderer.sharedMaterial.SetFloat("_MaxVal", visibilityWindow.y); + meshRenderer.sharedMaterial.SetFloat("_MinGradient", minGradient); + meshRenderer.sharedMaterial.SetFloat("_LightingGradientThresholdStart", gradientLightingThreshold.x); + meshRenderer.sharedMaterial.SetFloat("_LightingGradientThresholdEnd", gradientLightingThreshold.y); meshRenderer.sharedMaterial.SetVector("_TextureSize", new Vector3(dataset.dimX, dataset.dimY, dataset.dimZ)); if (rayTerminationEnabled) diff --git a/Assets/Shaders/DirectVolumeRenderingShader.shader b/Assets/Shaders/DirectVolumeRenderingShader.shader index 2a53b957..269faed5 100644 --- a/Assets/Shaders/DirectVolumeRenderingShader.shader +++ b/Assets/Shaders/DirectVolumeRenderingShader.shader @@ -8,6 +8,9 @@ _TFTex("Transfer Function Texture (Generated)", 2D) = "" {} _MinVal("Min val", Range(0.0, 1.0)) = 0.0 _MaxVal("Max val", Range(0.0, 1.0)) = 1.0 + _MinGradient("Gradient visibility threshold", Range(0.0, 1.0)) = 0.0 + _LightingGradientThresholdStart("Gradient threshold for lighting (end)", Range(0.0, 1.0)) = 0.0 + _LightingGradientThresholdEnd("Gradient threshold for lighting (start)", Range(0.0, 1.0)) = 0.0 [HideInInspector] _TextureSize("Dataset dimensions", Vector) = (1, 1, 1) } SubShader @@ -73,6 +76,10 @@ float _MaxVal; float3 _TextureSize; + float _MinGradient; + float _LightingGradientThresholdStart; + float _LightingGradientThresholdEnd; + #if CROSS_SECTION_ON #define CROSS_SECTION_TYPE_PLANE 1 #define CROSS_SECTION_TYPE_BOX_INCL 2 @@ -336,7 +343,9 @@ // Apply lighting #if defined(LIGHTING_ON) - src.rgb = calculateLighting(src.rgb, gradient / gradMag, getLightDirection(-ray.direction), -ray.direction, 0.3f); + float factor = smoothstep(_LightingGradientThresholdStart, _LightingGradientThresholdEnd, gradMag); + float3 shaded = calculateLighting(src.rgb, gradient / gradMag, getLightDirection(-ray.direction), -ray.direction, 0.3f); + src.rgb = lerp(src.rgb, shaded, factor); #endif src.rgb *= src.a; @@ -428,11 +437,16 @@ const float density = getDensity(currPos); if (density > _MinVal && density < _MaxVal) { - float3 normal = normalize(getGradient(currPos)); - col = getTF1DColour(density); - col.rgb = calculateLighting(col.rgb, normal, getLightDirection(-ray.direction), -ray.direction, 0.15); - col.a = 1.0f; - break; + float3 gradient = getGradient(currPos); + float gradMag = length(gradient); + if (gradMag > _MinGradient) + { + float3 normal = gradient / gradMag; + col = getTF1DColour(density); + col.rgb = calculateLighting(col.rgb, normal, getLightDirection(-ray.direction), -ray.direction, 0.15); + col.a = 1.0f; + break; + } } }