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 "UnityCG.cginc"
|
||||||
#include "TricubicSampling.cginc"
|
#include "TricubicSampling.cginc"
|
||||||
|
#include "DirectVolumeRendering.cginc"
|
||||||
#define AMBIENT_LIGHTING_FACTOR 0.5
|
|
||||||
#define JITTER_FACTOR 5.0
|
|
||||||
|
|
||||||
struct vert_in
|
struct vert_in
|
||||||
{
|
{
|
||||||
|
@ -54,220 +52,7 @@
|
||||||
float3 normal : NORMAL;
|
float3 normal : NORMAL;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct frag_out
|
frag_in vert(vert_in v)
|
||||||
{
|
|
||||||
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 o;
|
frag_in o;
|
||||||
o.vertex = UnityObjectToClipPos(v.vertex);
|
o.vertex = UnityObjectToClipPos(v.vertex);
|
||||||
|
@ -277,200 +62,73 @@
|
||||||
return o;
|
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)
|
frag_out frag(frag_in i)
|
||||||
{
|
{
|
||||||
#if MODE_DVR
|
#if MODE_DVR
|
||||||
return frag_dvr(i);
|
return frag_dvr(i.uv, i.vertexLocal);
|
||||||
#elif MODE_MIP
|
#elif MODE_MIP
|
||||||
return frag_mip(i);
|
return frag_mip(i.uv, i.vertexLocal);
|
||||||
#elif MODE_SURF
|
#elif MODE_SURF
|
||||||
return frag_surf(i);
|
return frag_surf(i.uv, i.vertexLocal);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ENDCG
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче