Merge pull request #91 from mlavik1/raymarching-improvements

Raymarching improvements
This commit is contained in:
Matias Lavik 2022-03-24 21:33:40 +01:00 коммит произвёл GitHub
Родитель 7f4fd3c35e 776639a5ea
Коммит 0985225f74
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
1 изменённых файлов: 111 добавлений и 57 удалений

Просмотреть файл

@ -68,12 +68,28 @@
float4x4 _CrossSectionMatrix;
#endif
float3 getRayDir(frag_in fragIn)
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(fragIn.vertexLocal, 0.0f)));
return normalize(ObjSpaceViewDir(float4(vertexLocal, 0.0f)));
}
else
{
@ -84,6 +100,56 @@
}
}
// 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)
{
@ -164,27 +230,24 @@
}
// Direct Volume Rendering
frag_out frag_dvr (frag_in i)
frag_out frag_dvr(frag_in i)
{
#define NUM_STEPS 512
#define MAX_NUM_STEPS 512
const float stepSize = 1.732f/*greatest distance in box*/ / NUM_STEPS;
RayInfo ray = getRayBack2Front(i.vertexLocal);
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
float3 rayStartPos = i.vertexLocal + float3(0.5f, 0.5f, 0.5f);
float3 lightDir = normalize(ObjSpaceViewDir(float4(float3(0.0f, 0.0f, 0.0f), 0.0f)));
float3 rayDir = getRayDir(i);
// Create a small random offset in order to remove artifacts
rayStartPos = rayStartPos + (2.0f * rayDir / NUM_STEPS) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
ray.startPos += (2.0f * 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);
uint iDepth = 0;
for (uint iStep = 0; iStep < NUM_STEPS; iStep++)
float tDepth = 0.0;
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
{
const float t = iStep * stepSize;
const float3 currPos = rayStartPos + rayDir * t;
if (currPos.x < 0.0f || currPos.x >= 1.0f || currPos.y < 0.0f || currPos.y > 1.0f || currPos.z < 0.0f || currPos.z > 1.0f) // TODO: avoid branch?
break;
const float t = iStep * raymarchInfo.numStepsRecip;
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
// Perform slice culling (cross section plane)
#ifdef CUTOUT_ON
@ -210,30 +273,26 @@
// Apply lighting
#ifdef LIGHTING_ON
src.rgb = calculateLighting(src.rgb, normalize(gradient), lightDir, rayDir, 0.3f);
src.rgb = calculateLighting(src.rgb, normalize(gradient), lightDir, ray.direction, 0.3f);
#endif
if (density < _MinVal || density > _MaxVal)
src.a = 0.0f;
// Optimisation: A branchless version of: if (density < _MinVal || density > _MaxVal) src.a = 0.0f;
src.a *= step(_MinVal, density) * step(density, _MaxVal);
col.rgb = src.a * src.rgb + (1.0f - src.a)*col.rgb;
col.a = src.a + (1.0f - src.a)*col.a;
if (src.a > 0.15f)
iDepth = iStep;
if (col.a > 1.0f)
break;
// Optimisation: A branchless version of: if (src.a > 0.15f) tDepth = t;
tDepth = max(tDepth, t * step(0.15, src.a));
}
// Write fragment output
frag_out output;
output.colour = col;
#if DEPTHWRITE_ON
if (iDepth != 0)
output.depth = localToDepth(rayStartPos + rayDir * (iDepth * stepSize) - float3(0.5f, 0.5f, 0.5f));
else
output.depth = 0;
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;
}
@ -241,36 +300,36 @@
// Maximum Intensity Projection mode
frag_out frag_mip(frag_in i)
{
#define NUM_STEPS 512
const float stepSize = 1.732f/*greatest distance in box*/ / NUM_STEPS;
#define MAX_NUM_STEPS 512
float3 rayStartPos = i.vertexLocal + float3(0.5f, 0.5f, 0.5f);
float3 rayDir = getRayDir(i);
RayInfo ray = getRayBack2Front(i.vertexLocal);
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
float maxDensity = 0.0f;
for (uint iStep = 0; iStep < NUM_STEPS; iStep++)
float3 maxDensityPos = ray.startPos;
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
{
const float t = iStep * stepSize;
const float3 currPos = rayStartPos + rayDir * t;
// Stop when we are outside the box
if (currPos.x < -0.0001f || currPos.x >= 1.0001f || currPos.y < -0.0001f || currPos.y > 1.0001f || currPos.z < -0.0001f || currPos.z > 1.0001f) // TODO: avoid branch?
break;
const float t = iStep * raymarchInfo.numStepsRecip;
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
#ifdef CUTOUT_ON
if (IsCutout(currPos))
continue;
#endif
const float density = getDensity(currPos);
if (density > _MinVal && density < _MaxVal)
maxDensity = max(density, maxDensity);
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(i.vertexLocal);
output.depth = localToDepth(maxDensityPos - float3(0.5f, 0.5f, 0.5f));
#endif
return output;
}
@ -279,27 +338,20 @@
// Draws the first point (closest to camera) with a density within the user-defined thresholds.
frag_out frag_surf(frag_in i)
{
#define NUM_STEPS 1024
const float stepSize = 1.732f/*greatest distance in box*/ / NUM_STEPS;
#define MAX_NUM_STEPS 1024
float3 rayStartPos = i.vertexLocal + float3(0.5f, 0.5f, 0.5f);
float3 rayDir = getRayDir(i);
// Start from the end, tand trace towards the vertex
rayStartPos += rayDir * stepSize * NUM_STEPS;
rayDir = -rayDir;
RayInfo ray = getRayFront2Back(i.vertexLocal);
RaymarchInfo raymarchInfo = initRaymarch(ray, MAX_NUM_STEPS);
// Create a small random offset in order to remove artifacts
rayStartPos = rayStartPos + (2.0f * rayDir / NUM_STEPS) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
ray.startPos = ray.startPos + (2.0f * ray.direction * raymarchInfo.stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
float4 col = float4(0,0,0,0);
for (uint iStep = 0; iStep < NUM_STEPS; iStep++)
for (int iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
{
const float t = iStep * stepSize;
const float3 currPos = rayStartPos + rayDir * t;
// Make sure we are inside the box
if (currPos.x < 0.0f || currPos.x >= 1.0f || currPos.y < 0.0f || currPos.y > 1.0f || currPos.z < 0.0f || currPos.z > 1.0f) // TODO: avoid branch?
continue;
const float t = iStep * raymarchInfo.numStepsRecip;
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
#ifdef CUTOUT_ON
if (IsCutout(currPos))
continue;
@ -310,7 +362,7 @@
{
float3 normal = normalize(getGradient(currPos));
col = getTF1DColour(density);
col.rgb = calculateLighting(col.rgb, normal, -rayDir, -rayDir, 0.15);
col.rgb = calculateLighting(col.rgb, normal, -ray.direction, -ray.direction, 0.15);
col.a = 1.0f;
break;
}
@ -320,7 +372,9 @@
frag_out output;
output.colour = col;
#if DEPTHWRITE_ON
output.depth = localToDepth(rayStartPos + rayDir * (iStep * stepSize) - float3(0.5f, 0.5f, 0.5f));
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;
}