Restructured shader and added support for shadow rendering
This commit is contained in:
Родитель
70ed1d1d94
Коммит
673a3ed152
|
@ -0,0 +1,396 @@
|
|||
#define AMBIENT_LIGHTING_FACTOR 0.5
|
||||
#define JITTER_FACTOR 5.0
|
||||
|
||||
struct frag_out
|
||||
{
|
||||
float4 colour : SV_TARGET;
|
||||
#if DEPTHWRITE_ON
|
||||
float depth : SV_DEPTH;
|
||||
#endif
|
||||
};
|
||||
|
||||
sampler3D _DataTex;
|
||||
sampler3D _GradientTex;
|
||||
sampler2D _NoiseTex;
|
||||
sampler2D _TFTex;
|
||||
|
||||
float _MinVal;
|
||||
float _MaxVal;
|
||||
float3 _TextureSize;
|
||||
|
||||
#if CROSS_SECTION_ON
|
||||
#define CROSS_SECTION_TYPE_PLANE 1
|
||||
#define CROSS_SECTION_TYPE_BOX_INCL 2
|
||||
#define CROSS_SECTION_TYPE_BOX_EXCL 3
|
||||
float4x4 _CrossSectionMatrices[8];
|
||||
float _CrossSectionTypes[8];
|
||||
int _NumCrossSections;
|
||||
#endif
|
||||
|
||||
struct RayInfo
|
||||
{
|
||||
float3 startPos;
|
||||
float3 endPos;
|
||||
float3 direction;
|
||||
float2 aabbInters;
|
||||
};
|
||||
|
||||
struct RaymarchInfo
|
||||
{
|
||||
RayInfo ray;
|
||||
int numSteps;
|
||||
float numStepsRecip;
|
||||
float stepSize;
|
||||
};
|
||||
|
||||
float3 getViewRayDir(float3 vertexLocal)
|
||||
{
|
||||
#ifdef SHADOW_CASTER_PASS
|
||||
return normalize(mul(unity_WorldToObject, _WorldSpaceLightPos0.xyz));
|
||||
#else
|
||||
if(unity_OrthoParams.w == 0)
|
||||
{
|
||||
// Perspective
|
||||
return normalize(ObjSpaceViewDir(float4(vertexLocal, 0.0f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Orthographic
|
||||
float3 camfwd = mul((float3x3)unity_CameraToWorld, float3(0,0,-1));
|
||||
float4 camfwdobjspace = mul(unity_WorldToObject, camfwd);
|
||||
return normalize(camfwdobjspace);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Find ray intersection points with axis aligned bounding box
|
||||
float2 intersectAABB(float3 rayOrigin, float3 rayDir, float3 boxMin, float3 boxMax)
|
||||
{
|
||||
float3 tMin = (boxMin - rayOrigin) / rayDir;
|
||||
float3 tMax = (boxMax - rayOrigin) / rayDir;
|
||||
float3 t1 = min(tMin, tMax);
|
||||
float3 t2 = max(tMin, tMax);
|
||||
float tNear = max(max(t1.x, t1.y), t1.z);
|
||||
float tFar = min(min(t2.x, t2.y), t2.z);
|
||||
return float2(tNear, tFar);
|
||||
};
|
||||
|
||||
// Get a ray for the specified fragment (back-to-front)
|
||||
RayInfo getRayBack2Front(float3 vertexLocal)
|
||||
{
|
||||
RayInfo ray;
|
||||
ray.direction = getViewRayDir(vertexLocal);
|
||||
ray.startPos = vertexLocal + float3(0.5f, 0.5f, 0.5f);
|
||||
// Find intersections with axis aligned boundinng box (the volume)
|
||||
ray.aabbInters = intersectAABB(ray.startPos, ray.direction, float3(0.0, 0.0, 0.0), float3(1.0f, 1.0f, 1.0));
|
||||
|
||||
// Check if camera is inside AABB
|
||||
const float3 farPos = ray.startPos + ray.direction * ray.aabbInters.y - float3(0.5f, 0.5f, 0.5f);
|
||||
float4 clipPos = UnityObjectToClipPos(float4(farPos, 1.0f));
|
||||
ray.aabbInters += min(clipPos.w, 0.0);
|
||||
|
||||
ray.endPos = ray.startPos + ray.direction * ray.aabbInters.y;
|
||||
return ray;
|
||||
}
|
||||
|
||||
// Get a ray for the specified fragment (front-to-back)
|
||||
RayInfo getRayFront2Back(float3 vertexLocal)
|
||||
{
|
||||
RayInfo ray = getRayBack2Front(vertexLocal);
|
||||
ray.direction = -ray.direction;
|
||||
float3 tmp = ray.startPos;
|
||||
ray.startPos = ray.endPos;
|
||||
ray.endPos = tmp;
|
||||
return ray;
|
||||
}
|
||||
|
||||
RaymarchInfo initRaymarch(RayInfo ray, int maxNumSteps)
|
||||
{
|
||||
RaymarchInfo raymarchInfo;
|
||||
raymarchInfo.stepSize = 1.732f/*greatest distance in box*/ / maxNumSteps;
|
||||
raymarchInfo.numSteps = (int)clamp(abs(ray.aabbInters.x - ray.aabbInters.y) / raymarchInfo.stepSize, 1, maxNumSteps);
|
||||
raymarchInfo.numStepsRecip = 1.0 / raymarchInfo.numSteps;
|
||||
return raymarchInfo;
|
||||
}
|
||||
|
||||
// Gets the colour from a 1D Transfer Function (x = density)
|
||||
float4 getTF1DColour(float density)
|
||||
{
|
||||
return tex2Dlod(_TFTex, float4(density, 0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Gets the colour from a 2D Transfer Function (x = density, y = gradient magnitude)
|
||||
float4 getTF2DColour(float density, float gradientMagnitude)
|
||||
{
|
||||
return tex2Dlod(_TFTex, float4(density, gradientMagnitude, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Gets the density at the specified position
|
||||
float getDensity(float3 pos)
|
||||
{
|
||||
#if CUBIC_INTERPOLATION_ON
|
||||
return interpolateTricubicFast(_DataTex, float3(pos.x, pos.y, pos.z), _TextureSize);
|
||||
#else
|
||||
return tex3Dlod(_DataTex, float4(pos.x, pos.y, pos.z, 0.0f));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Gets the gradient at the specified position
|
||||
float3 getGradient(float3 pos)
|
||||
{
|
||||
#if CUBIC_INTERPOLATION_ON
|
||||
return interpolateTricubicFast(_GradientTex, float3(pos.x, pos.y, pos.z), _TextureSize).rgb;
|
||||
#else
|
||||
return tex3Dlod(_GradientTex, float4(pos.x, pos.y, pos.z, 0.0f)).rgb;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get the light direction (using main light or view direction, based on setting)
|
||||
float3 getLightDirection(float3 viewDir)
|
||||
{
|
||||
#if defined(USE_MAIN_LIGHT)
|
||||
return normalize(mul(unity_WorldToObject, _WorldSpaceLightPos0.xyz));
|
||||
#else
|
||||
return viewDir;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Performs lighting calculations, and returns a modified colour.
|
||||
float3 calculateLighting(float3 col, float3 normal, float3 lightDir, float3 eyeDir, float specularIntensity)
|
||||
{
|
||||
// Invert normal if facing opposite direction of view direction.
|
||||
// Optimised version of: if(dot(normal, eyeDir) < 0.0) normal *= -1.0
|
||||
normal *= (step(0.0, dot(normal, eyeDir)) * 2.0 - 1.0);
|
||||
|
||||
float ndotl = max(lerp(0.0f, 1.5f, dot(normal, lightDir)), AMBIENT_LIGHTING_FACTOR);
|
||||
float3 diffuse = ndotl * col;
|
||||
float3 v = eyeDir;
|
||||
float3 r = normalize(reflect(-lightDir, normal));
|
||||
float rdotv = max( dot( r, v ), 0.0 );
|
||||
float3 specular = pow(rdotv, 32.0f) * float3(1.0f, 1.0f, 1.0f) * specularIntensity;
|
||||
return diffuse + specular;
|
||||
}
|
||||
|
||||
// Converts local position to depth value
|
||||
float localToDepth(float3 localPos)
|
||||
{
|
||||
float4 clipPos = UnityObjectToClipPos(float4(localPos, 1.0f));
|
||||
|
||||
#if defined(SHADER_API_GLCORE) || defined(SHADER_API_OPENGL) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)
|
||||
return (clipPos.z / clipPos.w) * 0.5 + 0.5;
|
||||
#else
|
||||
return clipPos.z / clipPos.w;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsCutout(float3 currPos)
|
||||
{
|
||||
#if CROSS_SECTION_ON
|
||||
// Move the reference in the middle of the mesh, like the pivot
|
||||
float4 pos = float4(currPos - float3(0.5f, 0.5f, 0.5f), 1.0f);
|
||||
|
||||
bool clipped = false;
|
||||
for (int i = 0; i < _NumCrossSections && !clipped; ++i)
|
||||
{
|
||||
const int type = (int)_CrossSectionTypes[i];
|
||||
const float4x4 mat = _CrossSectionMatrices[i];
|
||||
|
||||
// Convert from model space to plane's vector space
|
||||
float3 planeSpacePos = mul(mat, pos);
|
||||
if (type == CROSS_SECTION_TYPE_PLANE)
|
||||
clipped = planeSpacePos.z > 0.0f;
|
||||
else if(type == CROSS_SECTION_TYPE_BOX_INCL)
|
||||
clipped = !(planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f);
|
||||
else if(type == CROSS_SECTION_TYPE_BOX_EXCL)
|
||||
clipped = planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f;
|
||||
}
|
||||
return clipped;
|
||||
/*
|
||||
#if CUTOUT_PLANE
|
||||
return planeSpacePos.z > 0.0f;
|
||||
#elif CUTOUT_BOX_INCL
|
||||
return !(planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f);
|
||||
#elif CUTOUT_BOX_EXCL
|
||||
return planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f;
|
||||
#endif*/
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Direct Volume Rendering
|
||||
frag_out frag_dvr(float2 uv, float3 vertexLocal)
|
||||
{
|
||||
#define MAX_NUM_STEPS 512
|
||||
#define OPACITY_THRESHOLD (1.0 - 1.0 / 255.0)
|
||||
|
||||
#ifdef DVR_BACKWARD_ON
|
||||
RayInfo ray = getRayBack2Front(vertexLocal);
|
||||
#else
|
||||
RayInfo ray = getRayFront2Back(vertexLocal);
|
||||
#endif
|
||||
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
|
||||
|
||||
float3 lightDir = normalize(ObjSpaceViewDir(float4(float3(0.0f, 0.0f, 0.0f), 0.0f)));
|
||||
|
||||
// Create a small random offset in order to remove artifacts
|
||||
ray.startPos += (JITTER_FACTOR * ray.direction * raymarchInfo.stepSize) * tex2D(_NoiseTex, float2(uv.x, uv.y)).r;
|
||||
|
||||
float4 col = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
#ifdef DVR_BACKWARD_ON
|
||||
float tDepth = 0.0f;
|
||||
#else
|
||||
float tDepth = raymarchInfo.numStepsRecip * (raymarchInfo.numSteps - 1);
|
||||
#endif
|
||||
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
|
||||
{
|
||||
const float t = iStep * raymarchInfo.numStepsRecip;
|
||||
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
|
||||
|
||||
// Perform slice culling (cross section plane)
|
||||
#ifdef CROSS_SECTION_ON
|
||||
if(IsCutout(currPos))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Get the dansity/sample value of the current position
|
||||
const float density = getDensity(currPos);
|
||||
|
||||
// Apply visibility window
|
||||
if (density < _MinVal || density > _MaxVal) continue;
|
||||
|
||||
// Calculate gradient (needed for lighting and 2D transfer functions)
|
||||
#if defined(TF2D_ON) || defined(LIGHTING_ON)
|
||||
float3 gradient = getGradient(currPos);
|
||||
#endif
|
||||
|
||||
// Apply transfer function
|
||||
#if TF2D_ON
|
||||
float mag = length(gradient) / 1.75f;
|
||||
float4 src = getTF2DColour(density, mag);
|
||||
#else
|
||||
float4 src = getTF1DColour(density);
|
||||
#endif
|
||||
|
||||
// Apply lighting
|
||||
#if defined(LIGHTING_ON) && defined(DVR_BACKWARD_ON)
|
||||
src.rgb = calculateLighting(src.rgb, normalize(gradient), getLightDirection(ray.direction), ray.direction, 0.3f);
|
||||
#elif defined(LIGHTING_ON)
|
||||
src.rgb = calculateLighting(src.rgb, normalize(gradient), getLightDirection(-ray.direction), -ray.direction, 0.3f);
|
||||
#endif
|
||||
|
||||
#ifdef DVR_BACKWARD_ON
|
||||
col.rgb = src.a * src.rgb + (1.0f - src.a) * col.rgb;
|
||||
col.a = src.a + (1.0f - src.a) * col.a;
|
||||
|
||||
// Optimisation: A branchless version of: if (src.a > 0.15f) tDepth = t;
|
||||
tDepth = max(tDepth, t * step(0.15, src.a));
|
||||
#else
|
||||
src.rgb *= src.a;
|
||||
col = (1.0f - col.a) * src + col;
|
||||
|
||||
if (col.a > 0.15 && t < tDepth) {
|
||||
tDepth = t;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Early ray termination
|
||||
#if !defined(DVR_BACKWARD_ON) && defined(RAY_TERMINATE_ON)
|
||||
if (col.a > OPACITY_THRESHOLD) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Write fragment output
|
||||
frag_out output;
|
||||
output.colour = col;
|
||||
#if DEPTHWRITE_ON
|
||||
tDepth += (step(col.a, 0.0) * 1000.0); // Write large depth if no hit
|
||||
const float3 depthPos = lerp(ray.startPos, ray.endPos, tDepth) - float3(0.5f, 0.5f, 0.5f);
|
||||
output.depth = localToDepth(depthPos);
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
// Maximum Intensity Projection mode
|
||||
frag_out frag_mip(float2 uv, float3 vertexLocal)
|
||||
{
|
||||
#define MAX_NUM_STEPS 512
|
||||
|
||||
RayInfo ray = getRayBack2Front(vertexLocal);
|
||||
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
|
||||
|
||||
float maxDensity = 0.0f;
|
||||
float3 maxDensityPos = ray.startPos;
|
||||
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
|
||||
{
|
||||
const float t = iStep * raymarchInfo.numStepsRecip;
|
||||
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
|
||||
|
||||
#ifdef CROSS_SECTION_ON
|
||||
if (IsCutout(currPos))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
const float density = getDensity(currPos);
|
||||
if (density > maxDensity && density > _MinVal && density < _MaxVal)
|
||||
{
|
||||
maxDensity = density;
|
||||
maxDensityPos = currPos;
|
||||
}
|
||||
}
|
||||
|
||||
// Write fragment output
|
||||
frag_out output;
|
||||
output.colour = float4(1.0f, 1.0f, 1.0f, maxDensity); // maximum intensity
|
||||
#if DEPTHWRITE_ON
|
||||
output.depth = localToDepth(maxDensityPos - float3(0.5f, 0.5f, 0.5f));
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
// Surface rendering mode
|
||||
// Draws the first point (closest to camera) with a density within the user-defined thresholds.
|
||||
frag_out frag_surf(float2 uv, float3 vertexLocal)
|
||||
{
|
||||
#define MAX_NUM_STEPS 1024
|
||||
|
||||
RayInfo ray = getRayFront2Back(vertexLocal);
|
||||
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
|
||||
|
||||
// Create a small random offset in order to remove artifacts
|
||||
ray.startPos = ray.startPos + (JITTER_FACTOR * ray.direction * raymarchInfo.stepSize) * tex2D(_NoiseTex, float2(uv.x, uv.y)).r;
|
||||
|
||||
float4 col = float4(0,0,0,0);
|
||||
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
|
||||
{
|
||||
const float t = iStep * raymarchInfo.numStepsRecip;
|
||||
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
|
||||
|
||||
#ifdef CROSS_SECTION_ON
|
||||
if (IsCutout(currPos))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Write fragment output
|
||||
frag_out output;
|
||||
output.colour = col;
|
||||
#if DEPTHWRITE_ON
|
||||
|
||||
const float tDepth = iStep * raymarchInfo.numStepsRecip + (step(col.a, 0.0) * 1000.0); // Write large depth if no hit
|
||||
output.depth = localToDepth(lerp(ray.startPos, ray.endPos, tDepth) - float3(0.5f, 0.5f, 0.5f));
|
||||
#endif
|
||||
return output;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 66bb04e256c60266293bed645a2d8bb4
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -35,9 +35,7 @@
|
|||
|
||||
#include "UnityCG.cginc"
|
||||
#include "TricubicSampling.cginc"
|
||||
|
||||
#define AMBIENT_LIGHTING_FACTOR 0.5
|
||||
#define JITTER_FACTOR 5.0
|
||||
#include "DirectVolumeRendering.cginc"
|
||||
|
||||
struct vert_in
|
||||
{
|
||||
|
@ -54,220 +52,7 @@
|
|||
float3 normal : NORMAL;
|
||||
};
|
||||
|
||||
struct frag_out
|
||||
{
|
||||
float4 colour : SV_TARGET;
|
||||
#if DEPTHWRITE_ON
|
||||
float depth : SV_DEPTH;
|
||||
#endif
|
||||
};
|
||||
|
||||
sampler3D _DataTex;
|
||||
sampler3D _GradientTex;
|
||||
sampler2D _NoiseTex;
|
||||
sampler2D _TFTex;
|
||||
|
||||
float _MinVal;
|
||||
float _MaxVal;
|
||||
float3 _TextureSize;
|
||||
|
||||
#if CROSS_SECTION_ON
|
||||
#define CROSS_SECTION_TYPE_PLANE 1
|
||||
#define CROSS_SECTION_TYPE_BOX_INCL 2
|
||||
#define CROSS_SECTION_TYPE_BOX_EXCL 3
|
||||
float4x4 _CrossSectionMatrices[8];
|
||||
float _CrossSectionTypes[8];
|
||||
int _NumCrossSections;
|
||||
#endif
|
||||
|
||||
struct RayInfo
|
||||
{
|
||||
float3 startPos;
|
||||
float3 endPos;
|
||||
float3 direction;
|
||||
float2 aabbInters;
|
||||
};
|
||||
|
||||
struct RaymarchInfo
|
||||
{
|
||||
RayInfo ray;
|
||||
int numSteps;
|
||||
float numStepsRecip;
|
||||
float stepSize;
|
||||
};
|
||||
|
||||
float3 getViewRayDir(float3 vertexLocal)
|
||||
{
|
||||
if(unity_OrthoParams.w == 0)
|
||||
{
|
||||
// Perspective
|
||||
return normalize(ObjSpaceViewDir(float4(vertexLocal, 0.0f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Orthographic
|
||||
float3 camfwd = mul((float3x3)unity_CameraToWorld, float3(0,0,-1));
|
||||
float4 camfwdobjspace = mul(unity_WorldToObject, camfwd);
|
||||
return normalize(camfwdobjspace);
|
||||
}
|
||||
}
|
||||
|
||||
// Find ray intersection points with axis aligned bounding box
|
||||
float2 intersectAABB(float3 rayOrigin, float3 rayDir, float3 boxMin, float3 boxMax)
|
||||
{
|
||||
float3 tMin = (boxMin - rayOrigin) / rayDir;
|
||||
float3 tMax = (boxMax - rayOrigin) / rayDir;
|
||||
float3 t1 = min(tMin, tMax);
|
||||
float3 t2 = max(tMin, tMax);
|
||||
float tNear = max(max(t1.x, t1.y), t1.z);
|
||||
float tFar = min(min(t2.x, t2.y), t2.z);
|
||||
return float2(tNear, tFar);
|
||||
};
|
||||
|
||||
// Get a ray for the specified fragment (back-to-front)
|
||||
RayInfo getRayBack2Front(float3 vertexLocal)
|
||||
{
|
||||
RayInfo ray;
|
||||
ray.direction = getViewRayDir(vertexLocal);
|
||||
ray.startPos = vertexLocal + float3(0.5f, 0.5f, 0.5f);
|
||||
// Find intersections with axis aligned boundinng box (the volume)
|
||||
ray.aabbInters = intersectAABB(ray.startPos, ray.direction, float3(0.0, 0.0, 0.0), float3(1.0f, 1.0f, 1.0));
|
||||
|
||||
// Check if camera is inside AABB
|
||||
const float3 farPos = ray.startPos + ray.direction * ray.aabbInters.y - float3(0.5f, 0.5f, 0.5f);
|
||||
float4 clipPos = UnityObjectToClipPos(float4(farPos, 1.0f));
|
||||
ray.aabbInters += min(clipPos.w, 0.0);
|
||||
|
||||
ray.endPos = ray.startPos + ray.direction * ray.aabbInters.y;
|
||||
return ray;
|
||||
}
|
||||
|
||||
// Get a ray for the specified fragment (front-to-back)
|
||||
RayInfo getRayFront2Back(float3 vertexLocal)
|
||||
{
|
||||
RayInfo ray = getRayBack2Front(vertexLocal);
|
||||
ray.direction = -ray.direction;
|
||||
float3 tmp = ray.startPos;
|
||||
ray.startPos = ray.endPos;
|
||||
ray.endPos = tmp;
|
||||
return ray;
|
||||
}
|
||||
|
||||
RaymarchInfo initRaymarch(RayInfo ray, int maxNumSteps)
|
||||
{
|
||||
RaymarchInfo raymarchInfo;
|
||||
raymarchInfo.stepSize = 1.732f/*greatest distance in box*/ / maxNumSteps;
|
||||
raymarchInfo.numSteps = (int)clamp(abs(ray.aabbInters.x - ray.aabbInters.y) / raymarchInfo.stepSize, 1, maxNumSteps);
|
||||
raymarchInfo.numStepsRecip = 1.0 / raymarchInfo.numSteps;
|
||||
return raymarchInfo;
|
||||
}
|
||||
|
||||
// Gets the colour from a 1D Transfer Function (x = density)
|
||||
float4 getTF1DColour(float density)
|
||||
{
|
||||
return tex2Dlod(_TFTex, float4(density, 0.0f, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Gets the colour from a 2D Transfer Function (x = density, y = gradient magnitude)
|
||||
float4 getTF2DColour(float density, float gradientMagnitude)
|
||||
{
|
||||
return tex2Dlod(_TFTex, float4(density, gradientMagnitude, 0.0f, 0.0f));
|
||||
}
|
||||
|
||||
// Gets the density at the specified position
|
||||
float getDensity(float3 pos)
|
||||
{
|
||||
#if CUBIC_INTERPOLATION_ON
|
||||
return interpolateTricubicFast(_DataTex, float3(pos.x, pos.y, pos.z), _TextureSize);
|
||||
#else
|
||||
return tex3Dlod(_DataTex, float4(pos.x, pos.y, pos.z, 0.0f));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Gets the gradient at the specified position
|
||||
float3 getGradient(float3 pos)
|
||||
{
|
||||
#if CUBIC_INTERPOLATION_ON
|
||||
return interpolateTricubicFast(_GradientTex, float3(pos.x, pos.y, pos.z), _TextureSize).rgb;
|
||||
#else
|
||||
return tex3Dlod(_GradientTex, float4(pos.x, pos.y, pos.z, 0.0f)).rgb;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Get the light direction (using main light or view direction, based on setting)
|
||||
float3 getLightDirection(float3 viewDir)
|
||||
{
|
||||
#if defined(USE_MAIN_LIGHT)
|
||||
return normalize(mul(unity_WorldToObject, _WorldSpaceLightPos0.xyz));
|
||||
#else
|
||||
return viewDir;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Performs lighting calculations, and returns a modified colour.
|
||||
float3 calculateLighting(float3 col, float3 normal, float3 lightDir, float3 eyeDir, float specularIntensity)
|
||||
{
|
||||
// Invert normal if facing opposite direction of view direction.
|
||||
// Optimised version of: if(dot(normal, eyeDir) < 0.0) normal *= -1.0
|
||||
normal *= (step(0.0, dot(normal, eyeDir)) * 2.0 - 1.0);
|
||||
|
||||
float ndotl = max(lerp(0.0f, 1.5f, dot(normal, lightDir)), AMBIENT_LIGHTING_FACTOR);
|
||||
float3 diffuse = ndotl * col;
|
||||
float3 v = eyeDir;
|
||||
float3 r = normalize(reflect(-lightDir, normal));
|
||||
float rdotv = max( dot( r, v ), 0.0 );
|
||||
float3 specular = pow(rdotv, 32.0f) * float3(1.0f, 1.0f, 1.0f) * specularIntensity;
|
||||
return diffuse + specular;
|
||||
}
|
||||
|
||||
// Converts local position to depth value
|
||||
float localToDepth(float3 localPos)
|
||||
{
|
||||
float4 clipPos = UnityObjectToClipPos(float4(localPos, 1.0f));
|
||||
|
||||
#if defined(SHADER_API_GLCORE) || defined(SHADER_API_OPENGL) || defined(SHADER_API_GLES) || defined(SHADER_API_GLES3)
|
||||
return (clipPos.z / clipPos.w) * 0.5 + 0.5;
|
||||
#else
|
||||
return clipPos.z / clipPos.w;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsCutout(float3 currPos)
|
||||
{
|
||||
#if CROSS_SECTION_ON
|
||||
// Move the reference in the middle of the mesh, like the pivot
|
||||
float4 pos = float4(currPos - float3(0.5f, 0.5f, 0.5f), 1.0f);
|
||||
|
||||
bool clipped = false;
|
||||
for (int i = 0; i < _NumCrossSections && !clipped; ++i)
|
||||
{
|
||||
const int type = (int)_CrossSectionTypes[i];
|
||||
const float4x4 mat = _CrossSectionMatrices[i];
|
||||
|
||||
// Convert from model space to plane's vector space
|
||||
float3 planeSpacePos = mul(mat, pos);
|
||||
if (type == CROSS_SECTION_TYPE_PLANE)
|
||||
clipped = planeSpacePos.z > 0.0f;
|
||||
else if(type == CROSS_SECTION_TYPE_BOX_INCL)
|
||||
clipped = !(planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f);
|
||||
else if(type == CROSS_SECTION_TYPE_BOX_EXCL)
|
||||
clipped = planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f;
|
||||
}
|
||||
return clipped;
|
||||
/*
|
||||
#if CUTOUT_PLANE
|
||||
return planeSpacePos.z > 0.0f;
|
||||
#elif CUTOUT_BOX_INCL
|
||||
return !(planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f);
|
||||
#elif CUTOUT_BOX_EXCL
|
||||
return planeSpacePos.x >= -0.5f && planeSpacePos.x <= 0.5f && planeSpacePos.y >= -0.5f && planeSpacePos.y <= 0.5f && planeSpacePos.z >= -0.5f && planeSpacePos.z <= 0.5f;
|
||||
#endif*/
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
frag_in vert_main (vert_in v)
|
||||
frag_in vert(vert_in v)
|
||||
{
|
||||
frag_in o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
|
@ -277,200 +62,73 @@
|
|||
return o;
|
||||
}
|
||||
|
||||
// Direct Volume Rendering
|
||||
frag_out frag_dvr(frag_in i)
|
||||
{
|
||||
#define MAX_NUM_STEPS 512
|
||||
#define OPACITY_THRESHOLD (1.0 - 1.0 / 255.0)
|
||||
|
||||
#ifdef DVR_BACKWARD_ON
|
||||
RayInfo ray = getRayBack2Front(i.vertexLocal);
|
||||
#else
|
||||
RayInfo ray = getRayFront2Back(i.vertexLocal);
|
||||
#endif
|
||||
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
|
||||
|
||||
float3 lightDir = normalize(ObjSpaceViewDir(float4(float3(0.0f, 0.0f, 0.0f), 0.0f)));
|
||||
|
||||
// Create a small random offset in order to remove artifacts
|
||||
ray.startPos += (JITTER_FACTOR * ray.direction * raymarchInfo.stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
|
||||
|
||||
float4 col = float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
#ifdef DVR_BACKWARD_ON
|
||||
float tDepth = 0.0f;
|
||||
#else
|
||||
float tDepth = raymarchInfo.numStepsRecip * (raymarchInfo.numSteps - 1);
|
||||
#endif
|
||||
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
|
||||
{
|
||||
const float t = iStep * raymarchInfo.numStepsRecip;
|
||||
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
|
||||
|
||||
// Perform slice culling (cross section plane)
|
||||
#ifdef CROSS_SECTION_ON
|
||||
if(IsCutout(currPos))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
// Get the dansity/sample value of the current position
|
||||
const float density = getDensity(currPos);
|
||||
|
||||
// Apply visibility window
|
||||
if (density < _MinVal || density > _MaxVal) continue;
|
||||
|
||||
// Calculate gradient (needed for lighting and 2D transfer functions)
|
||||
#if defined(TF2D_ON) || defined(LIGHTING_ON)
|
||||
float3 gradient = getGradient(currPos);
|
||||
#endif
|
||||
|
||||
// Apply transfer function
|
||||
#if TF2D_ON
|
||||
float mag = length(gradient) / 1.75f;
|
||||
float4 src = getTF2DColour(density, mag);
|
||||
#else
|
||||
float4 src = getTF1DColour(density);
|
||||
#endif
|
||||
|
||||
// Apply lighting
|
||||
#if defined(LIGHTING_ON) && defined(DVR_BACKWARD_ON)
|
||||
src.rgb = calculateLighting(src.rgb, normalize(gradient), getLightDirection(ray.direction), ray.direction, 0.3f);
|
||||
#elif defined(LIGHTING_ON)
|
||||
src.rgb = calculateLighting(src.rgb, normalize(gradient), getLightDirection(-ray.direction), -ray.direction, 0.3f);
|
||||
#endif
|
||||
|
||||
#ifdef DVR_BACKWARD_ON
|
||||
col.rgb = src.a * src.rgb + (1.0f - src.a) * col.rgb;
|
||||
col.a = src.a + (1.0f - src.a) * col.a;
|
||||
|
||||
// Optimisation: A branchless version of: if (src.a > 0.15f) tDepth = t;
|
||||
tDepth = max(tDepth, t * step(0.15, src.a));
|
||||
#else
|
||||
src.rgb *= src.a;
|
||||
col = (1.0f - col.a) * src + col;
|
||||
|
||||
if (col.a > 0.15 && t < tDepth) {
|
||||
tDepth = t;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Early ray termination
|
||||
#if !defined(DVR_BACKWARD_ON) && defined(RAY_TERMINATE_ON)
|
||||
if (col.a > OPACITY_THRESHOLD) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// Write fragment output
|
||||
frag_out output;
|
||||
output.colour = col;
|
||||
#if DEPTHWRITE_ON
|
||||
tDepth += (step(col.a, 0.0) * 1000.0); // Write large depth if no hit
|
||||
const float3 depthPos = lerp(ray.startPos, ray.endPos, tDepth) - float3(0.5f, 0.5f, 0.5f);
|
||||
output.depth = localToDepth(depthPos);
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
// Maximum Intensity Projection mode
|
||||
frag_out frag_mip(frag_in i)
|
||||
{
|
||||
#define MAX_NUM_STEPS 512
|
||||
|
||||
RayInfo ray = getRayBack2Front(i.vertexLocal);
|
||||
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
|
||||
|
||||
float maxDensity = 0.0f;
|
||||
float3 maxDensityPos = ray.startPos;
|
||||
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
|
||||
{
|
||||
const float t = iStep * raymarchInfo.numStepsRecip;
|
||||
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
|
||||
|
||||
#ifdef CROSS_SECTION_ON
|
||||
if (IsCutout(currPos))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
const float density = getDensity(currPos);
|
||||
if (density > maxDensity && density > _MinVal && density < _MaxVal)
|
||||
{
|
||||
maxDensity = density;
|
||||
maxDensityPos = currPos;
|
||||
}
|
||||
}
|
||||
|
||||
// Write fragment output
|
||||
frag_out output;
|
||||
output.colour = float4(1.0f, 1.0f, 1.0f, maxDensity); // maximum intensity
|
||||
#if DEPTHWRITE_ON
|
||||
output.depth = localToDepth(maxDensityPos - float3(0.5f, 0.5f, 0.5f));
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
// Surface rendering mode
|
||||
// Draws the first point (closest to camera) with a density within the user-defined thresholds.
|
||||
frag_out frag_surf(frag_in i)
|
||||
{
|
||||
#define MAX_NUM_STEPS 1024
|
||||
|
||||
RayInfo ray = getRayFront2Back(i.vertexLocal);
|
||||
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
|
||||
|
||||
// Create a small random offset in order to remove artifacts
|
||||
ray.startPos = ray.startPos + (JITTER_FACTOR * ray.direction * raymarchInfo.stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
|
||||
|
||||
float4 col = float4(0,0,0,0);
|
||||
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
|
||||
{
|
||||
const float t = iStep * raymarchInfo.numStepsRecip;
|
||||
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
|
||||
|
||||
#ifdef CROSS_SECTION_ON
|
||||
if (IsCutout(currPos))
|
||||
continue;
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Write fragment output
|
||||
frag_out output;
|
||||
output.colour = col;
|
||||
#if DEPTHWRITE_ON
|
||||
|
||||
const float tDepth = iStep * raymarchInfo.numStepsRecip + (step(col.a, 0.0) * 1000.0); // Write large depth if no hit
|
||||
output.depth = localToDepth(lerp(ray.startPos, ray.endPos, tDepth) - float3(0.5f, 0.5f, 0.5f));
|
||||
#endif
|
||||
return output;
|
||||
}
|
||||
|
||||
frag_in vert(vert_in v)
|
||||
{
|
||||
return vert_main(v);
|
||||
}
|
||||
|
||||
frag_out frag(frag_in i)
|
||||
{
|
||||
#if MODE_DVR
|
||||
return frag_dvr(i);
|
||||
return frag_dvr(i.uv, i.vertexLocal);
|
||||
#elif MODE_MIP
|
||||
return frag_mip(i);
|
||||
return frag_mip(i.uv, i.vertexLocal);
|
||||
#elif MODE_SURF
|
||||
return frag_surf(i);
|
||||
return frag_surf(i.uv, i.vertexLocal);
|
||||
#endif
|
||||
}
|
||||
|
||||
ENDCG
|
||||
}
|
||||
|
||||
Pass
|
||||
{
|
||||
Tags {"LightMode"="ShadowCaster"}
|
||||
|
||||
CGPROGRAM
|
||||
#pragma multi_compile MODE_DVR MODE_MIP MODE_SURF
|
||||
#pragma multi_compile __ TF2D_ON
|
||||
#pragma multi_compile __ CROSS_SECTION_ON
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
#pragma multi_compile_shadowcaster
|
||||
#include "UnityCG.cginc"
|
||||
#define SHADOW_CASTER_PASS 1
|
||||
#include "DirectVolumeRendering.cginc"
|
||||
|
||||
struct vert_in
|
||||
{
|
||||
float4 vertex : POSITION;
|
||||
float4 normal : NORMAL;
|
||||
float2 uv : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct frag_in
|
||||
{
|
||||
float4 vertex : SV_POSITION;
|
||||
float2 uv : TEXCOORD0;
|
||||
float3 vertexLocal : TEXCOORD1;
|
||||
float3 normal : NORMAL;
|
||||
};
|
||||
|
||||
frag_in vert(vert_in v)
|
||||
{
|
||||
frag_in o;
|
||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||
o.uv = v.uv;
|
||||
o.vertexLocal = v.vertex;
|
||||
o.normal = UnityObjectToWorldNormal(v.normal);
|
||||
return o;
|
||||
}
|
||||
|
||||
float4 frag(frag_in i) : SV_Target
|
||||
{
|
||||
#if MODE_DVR
|
||||
frag_out output = frag_dvr(i.uv, i.vertexLocal);
|
||||
#elif MODE_MIP
|
||||
frag_out output = frag_mip(i.uv, i.vertexLocal);
|
||||
#elif MODE_SURF
|
||||
frag_out output = frag_surf(i.uv, i.vertexLocal);
|
||||
#endif
|
||||
clip(output.colour.a - 0.01);
|
||||
SHADOW_CASTER_FRAGMENT(i)
|
||||
}
|
||||
ENDCG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче