Skip to content

Commit

Permalink
Enable to rotate slice axes.
Browse files Browse the repository at this point in the history
  • Loading branch information
mattatz committed Jun 30, 2018
1 parent 99b6817 commit 0f5e1a8
Show file tree
Hide file tree
Showing 11 changed files with 371 additions and 231 deletions.
250 changes: 148 additions & 102 deletions Assets/VolumeRendering/Demo.unity

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions Assets/VolumeRendering/Scripts/Axis.cs
@@ -0,0 +1,31 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace VolumeRendering
{

public class Axis : MonoBehaviour {

[SerializeField] protected Transform root;
[SerializeField] protected float length = 5f;

protected void OnDrawGizmos()
{
Gizmos.matrix = Matrix4x4.Rotate(root.rotation) * Matrix4x4.Rotate(transform.rotation).inverse;

Gizmos.color = Color.red;
Gizmos.DrawLine(Vector3.zero, Vector3.right * length);

Gizmos.color = Color.green;
Gizmos.DrawLine(Vector3.zero, Vector3.up * length);

Gizmos.color = Color.blue;
Gizmos.DrawLine(Vector3.zero, Vector3.forward * length);
}

}

}


13 changes: 13 additions & 0 deletions Assets/VolumeRendering/Scripts/Axis.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Assets/VolumeRendering/Scripts/VolumeRendering.cs
Expand Up @@ -23,6 +23,7 @@ public class VolumeRendering : MonoBehaviour {
[Range(0f, 1f)] public float sliceXMin = 0.0f, sliceXMax = 1.0f;
[Range(0f, 1f)] public float sliceYMin = 0.0f, sliceYMax = 1.0f;
[Range(0f, 1f)] public float sliceZMin = 0.0f, sliceZMax = 1.0f;
public Quaternion axis = Quaternion.identity;

[SerializeField] protected Texture3D volume;

Expand All @@ -39,6 +40,8 @@ public class VolumeRendering : MonoBehaviour {
material.SetFloat("_Intensity", intensity);
material.SetVector("_SliceMin", new Vector3(sliceXMin, sliceYMin, sliceZMin));
material.SetVector("_SliceMax", new Vector3(sliceXMax, sliceYMax, sliceZMax));

material.SetMatrix("_AxisRotationMatrix", Matrix4x4.Rotate(axis));
}

Mesh Build() {
Expand Down
6 changes: 6 additions & 0 deletions Assets/VolumeRendering/Scripts/VolumeRenderingController.cs
Expand Up @@ -11,6 +11,7 @@ public class VolumeRenderingController : MonoBehaviour {

[SerializeField] protected VolumeRendering volume;
[SerializeField] protected Slider sliderXMin, sliderXMax, sliderYMin, sliderYMax, sliderZMin, sliderZMax;
[SerializeField] protected Transform axis;

void Start ()
{
Expand Down Expand Up @@ -38,6 +39,11 @@ void Start ()
});
}

void Update()
{
volume.axis = axis.rotation;
}

public void OnIntensity(float v)
{
volume.intensity = v;
Expand Down
145 changes: 145 additions & 0 deletions Assets/VolumeRendering/Shaders/VolumeRendering.cginc
@@ -0,0 +1,145 @@
#ifndef __VOLUME_RENDERING_INCLUDED__
#define __VOLUME_RENDERING_INCLUDED__

#include "UnityCG.cginc"

#ifndef ITERATIONS
#define ITERATIONS 100
#endif

half4 _Color;
sampler3D _Volume;
half _Intensity, _Threshold;
half3 _SliceMin, _SliceMax;
float4x4 _AxisRotationMatrix;

struct Ray {
float3 origin;
float3 dir;
};

struct AABB {
float3 min;
float3 max;
};

bool intersect(Ray r, AABB aabb, out float t0, out float t1)
{
float3 invR = 1.0 / r.dir;
float3 tbot = invR * (aabb.min - r.origin);
float3 ttop = invR * (aabb.max - r.origin);
float3 tmin = min(ttop, tbot);
float3 tmax = max(ttop, tbot);
float2 t = max(tmin.xx, tmin.yz);
t0 = max(t.x, t.y);
t = min(tmax.xx, tmax.yz);
t1 = min(t.x, t.y);
return t0 <= t1;
}

float3 localize(float3 p) {
return mul(unity_WorldToObject, float4(p, 1)).xyz;
}

float3 get_uv(float3 p) {
// float3 local = localize(p);
return (p + 0.5);
}

float sample_volume(float3 uv, float3 p)
{
float v = tex3D(_Volume, uv).r * _Intensity;

float3 axis = mul(_AxisRotationMatrix, float4(p, 0)).xyz;
axis = get_uv(axis);
float min = step(_SliceMin.x, axis.x) * step(_SliceMin.y, axis.y) * step(_SliceMin.z, axis.z);
float max = step(axis.x, _SliceMax.x) * step(axis.y, _SliceMax.y) * step(axis.z, _SliceMax.z);

return v * min * max;
}

bool outside(float3 uv)
{
const float EPSILON = 0.01;
float lower = -EPSILON;
float upper = 1 + EPSILON;
return (
uv.x < lower || uv.y < lower || uv.z < lower ||
uv.x > upper || uv.y > upper || uv.z > upper
);
}

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 world : TEXCOORD1;
};

v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.world = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}

fixed4 frag(v2f i) : SV_Target
{
Ray ray;
ray.origin = localize(i.world);

// world space direction to object space
float3 dir = normalize(i.world - _WorldSpaceCameraPos);
ray.dir = normalize(mul((float3x3) unity_WorldToObject, dir));

AABB aabb;
aabb.min = float3(-0.5, -0.5, -0.5);
aabb.max = float3(0.5, 0.5, 0.5);

float tnear;
float tfar;
intersect(ray, aabb, tnear, tfar);

tnear = max(0.0, tnear);

// float3 start = ray.origin + ray.dir * tnear;
float3 start = ray.origin;
float3 end = ray.origin + ray.dir * tfar;
float dist = abs(tfar - tnear); // float dist = distance(start, end);
float step_size = dist / float(ITERATIONS);
float3 ds = normalize(end - start) * step_size;

float4 dst = float4(0, 0, 0, 0);
float3 p = start;

[unroll]
for (int iter = 0; iter < ITERATIONS; iter++)
{
float3 uv = get_uv(p);
float v = sample_volume(uv, p);
float4 src = float4(v, v, v, v);
src.a *= 0.5;
src.rgb *= src.a;

// blend
dst = (1.0 - dst.a) * src + dst;
p += ds;

if (dst.a > _Threshold)
{
break;
}
}

return saturate(dst) * _Color;
}

#endif
10 changes: 10 additions & 0 deletions Assets/VolumeRendering/Shaders/VolumeRendering.cginc.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

131 changes: 3 additions & 128 deletions Assets/VolumeRendering/Shaders/VolumeRendering.shader
Expand Up @@ -12,83 +12,6 @@

CGINCLUDE

half4 _Color;
sampler3D _Volume;
half _Intensity, _Threshold;
half3 _SliceMin, _SliceMax;

struct Ray {
float3 origin;
float3 dir;
};

struct AABB {
float3 min;
float3 max;
};

bool intersect(Ray r, AABB aabb, out float t0, out float t1)
{
float3 invR = 1.0 / r.dir;
float3 tbot = invR * (aabb.min - r.origin);
float3 ttop = invR * (aabb.max - r.origin);
float3 tmin = min(ttop, tbot);
float3 tmax = max(ttop, tbot);
float2 t = max(tmin.xx, tmin.yz);
t0 = max(t.x, t.y);
t = min(tmax.xx, tmax.yz);
t1 = min(t.x, t.y);
return t0 <= t1;
}

float3 localize(float3 p) {
return mul(unity_WorldToObject, float4(p, 1)).xyz;
}

float3 get_uv(float3 p) {
// float3 local = localize(p);
return (p + 0.5);
}

float sample_volume(float3 uv) {
float v = tex3D(_Volume, uv).r * _Intensity;
float min = step(_SliceMin.x, uv.x) * step(_SliceMin.y, uv.y) * step(_SliceMin.z, uv.z);
float max = step(uv.x, _SliceMax.x) * step(uv.y, _SliceMax.y) * step(uv.z, _SliceMax.z);
return v * min * max;
}

bool outside(float3 uv) {
const float EPSILON = 0.01;
float lower = -EPSILON;
float upper = 1 + EPSILON;
return (
uv.x < lower || uv.y < lower || uv.z < lower ||
uv.x > upper || uv.y > upper || uv.z > upper
);
}

struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
float3 world : TEXCOORD1;
};

v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.world = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}

ENDCG

SubShader {
Expand All @@ -99,60 +22,12 @@
Pass
{
CGPROGRAM

#define ITERATIONS 100
#include "./VolumeRendering.cginc"
#pragma vertex vert
#pragma fragment frag

#define COUNT 100

fixed4 frag (v2f i) : SV_Target
{
Ray ray;
ray.origin = localize(i.world);

// world space direction to object space
float3 dir = normalize(i.world - _WorldSpaceCameraPos);
ray.dir = normalize(mul((float3x3)unity_WorldToObject, dir));

AABB aabb;
aabb.min = float3(-0.5, -0.5, -0.5);
aabb.max = float3( 0.5, 0.5, 0.5);

float tnear;
float tfar;
intersect(ray, aabb, tnear, tfar);

tnear = max(0.0, tnear);

// float3 start = ray.origin + ray.dir * tnear;
float3 start = ray.origin;
float3 end = ray.origin + ray.dir * tfar;
float dist = abs(tfar - tnear); // float dist = distance(start, end);
float step_size = dist / float(COUNT);
float3 ds = normalize(end - start) * step_size;

float4 dst = float4(0, 0, 0, 0);
float3 p = start;

[unroll]
for (int iter = 0; iter < COUNT; iter++) {
float3 uv = get_uv(p);
float v = sample_volume(uv);
float4 src = float4(v, v, v, v);
src.a *= 0.5;
src.rgb *= src.a;

// blend
dst = (1.0 - dst.a) * src + dst;
p += ds;

if (dst.a > _Threshold) {
break;
}
}

return saturate(dst) * _Color;
}

ENDCG
}
}
Expand Down
Binary file added Captures/Axis.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion ProjectSettings/ProjectVersion.txt
@@ -1 +1 @@
m_EditorVersion: 2017.2.0f3
m_EditorVersion: 2017.3.1f1

0 comments on commit 0f5e1a8

Please sign in to comment.