Handle camera inside AABB when raymarching

This commit is contained in:
Matias Lavik 2022-03-24 15:59:36 +01:00
Родитель 08ab58c4ae
Коммит 74c1ed2c5d
1 изменённых файлов: 87 добавлений и 43 удалений

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

@ -68,12 +68,27 @@
float4x4 _CrossSectionMatrix;
#endif
float3 getViewRayDir(frag_in fragIn)
struct RayInfo
{
float3 startPos;
float3 endPos;
float3 direction;
float2 aabbInters;
};
struct RaymarchInfo
{
RayInfo ray;
uint numSteps;
float numStepsRecip;
};
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 +99,7 @@
}
}
// Find ray intersection points with axis aligned bounding box
float2 intersectAABB(float3 rayOrigin, float3 rayDir, float3 boxMin, float3 boxMax)
{
float3 tMin = (boxMin - rayOrigin) / rayDir;
@ -95,6 +111,51 @@
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;
//float3 vertPos = vertexLocal + float3(0.5f, 0.5f, 0.5f);
//float3 viewRayDir = getViewRayDir(vertexLocal);
// Find intersections with axis aligned boundinng box (the volume)
//ray.aabbInters = intersectAABB(vertPos, viewRayDir, float3(0.0, 0.0, 0.0), float3(1.0f, 1.0f, 1.0));
//ray.direction = -viewRayDir;
//ray.startPos = vertPos + viewRayDir * ray.aabbInters.y;
//ray.endPos = vertPos;
return ray;
}
RaymarchInfo initRaymarch(RayInfo ray, float stepSize)
{
RaymarchInfo raymarchInfo;
raymarchInfo.numSteps = (uint)(abs(ray.aabbInters.x - ray.aabbInters.y) / stepSize);
raymarchInfo.numStepsRecip = 1.0 / raymarchInfo.numSteps;
return raymarchInfo;
}
// Gets the colour from a 1D Transfer Function (x = density)
float4 getTF1DColour(float density)
{
@ -180,25 +241,20 @@
#define MAX_NUM_STEPS 512
const float stepSize = 1.732f/*greatest distance in box*/ / MAX_NUM_STEPS;
RayInfo ray = getRayBack2Front(i.vertexLocal);
RaymarchInfo raymarchInfo = initRaymarch(ray, stepSize);
float3 lightDir = normalize(ObjSpaceViewDir(float4(float3(0.0f, 0.0f, 0.0f), 0.0f)));
float3 rayDir = getViewRayDir(i);
float3 rayStartPos = i.vertexLocal + float3(0.5f, 0.5f, 0.5f);
float2 aabbInters = intersectAABB(rayStartPos, rayDir, float3(0.0, 0.0, 0.0), float3(1.0f, 1.0f, 1.0));
float3 rayEndPos = rayStartPos + rayDir * aabbInters.y;
const uint numSteps = (uint)(abs(aabbInters.x - aabbInters.y) / stepSize);
const float numStepsRecip = 1.0 / numSteps;
// Create a small random offset in order to remove artifacts
rayStartPos += (2.0f * rayDir * stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
ray.startPos += (2.0f * ray.direction * stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
float4 col = float4(0.0f, 0.0f, 0.0f, 0.0f);
float tDepth = 0.0;
for (uint iStep = 0; iStep < numSteps; iStep++)
for (uint iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
{
const float t = iStep * numStepsRecip;
const float3 currPos = lerp(rayStartPos, rayEndPos, t);
const float t = iStep * raymarchInfo.numStepsRecip;
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
// Perform slice culling (cross section plane)
#ifdef CUTOUT_ON
@ -224,7 +280,7 @@
// 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
// Optimisation: A branchless version of: if (density < _MinVal || density > _MaxVal) src.a = 0.0f;
@ -244,7 +300,7 @@
frag_out output;
output.colour = col;
#if DEPTHWRITE_ON
const float3 depthPos = lerp(rayStartPos, rayEndPos, tDepth) - float3(0.5f, 0.5f, 0.5f);
const float3 depthPos = lerp(ray.startPos, ray.endPos, tDepth) - float3(0.5f, 0.5f, 0.5f);
output.depth = localToDepth(depthPos) * step(0.0, tDepth); // Write 0 if tDepth is zero
#endif
return output;
@ -256,19 +312,15 @@
#define MAX_NUM_STEPS 512
const float stepSize = 1.732f/*greatest distance in box*/ / MAX_NUM_STEPS;
float3 rayStartPos = i.vertexLocal + float3(0.5f, 0.5f, 0.5f);
float3 rayDir = getViewRayDir(i);
float2 aabbInters = intersectAABB(rayStartPos, rayDir, float3(0.0, 0.0, 0.0), float3(1.0f, 1.0f, 1.0));
float3 rayEndPos = rayStartPos + rayDir * aabbInters.y;
const uint numSteps = (uint)(abs(aabbInters.x - aabbInters.y) / stepSize);
const float numStepsRecip = 1.0 / numSteps;
RayInfo ray = getRayBack2Front(i.vertexLocal);
RaymarchInfo raymarchInfo = initRaymarch(ray, stepSize);
float maxDensity = 0.0f;
float3 maxDensityPos = rayStartPos;
for (uint iStep = 0; iStep < numSteps; iStep++)
float3 maxDensityPos = ray.startPos;
for (uint iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
{
const float t = iStep * numStepsRecip;
const float3 currPos = lerp(rayStartPos, rayEndPos, t);
const float t = iStep * raymarchInfo.numStepsRecip;
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
#ifdef CUTOUT_ON
if (IsCutout(currPos))
@ -298,26 +350,18 @@
{
#define MAX_NUM_STEPS 1024
const float stepSize = 1.732f/*greatest distance in box*/ / MAX_NUM_STEPS;
float3 vertPos = i.vertexLocal + float3(0.5f, 0.5f, 0.5f);
float3 viewRayDir = getViewRayDir(i);
// Find intersections with axis aligned boundinng box (the volume)
float2 aabbInters = intersectAABB(vertPos, viewRayDir, float3(0.0, 0.0, 0.0), float3(1.0f, 1.0f, 1.0));
// Front-to-back tracing
float3 rayDir = -viewRayDir;
float3 rayStartPos = vertPos + viewRayDir * aabbInters.y;
float3 rayEndPos = vertPos;
RayInfo ray = getRayFront2Back(i.vertexLocal);
RaymarchInfo raymarchInfo = initRaymarch(ray, stepSize);
// Create a small random offset in order to remove artifacts
rayStartPos = rayStartPos + (2.0f * rayDir * stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
const uint numSteps = (uint)(abs(aabbInters.x - aabbInters.y) / stepSize);
const float numStepsRecip = 1.0 / numSteps;
ray.startPos = ray.startPos + (2.0f * ray.direction * stepSize) * tex2D(_NoiseTex, float2(i.uv.x, i.uv.y)).r;
float4 col = float4(0,0,0,0);
for (uint iStep = 0; iStep < numSteps; iStep++)
for (uint iStep = 0; iStep < raymarchInfo.numSteps; iStep++)
{
const float t = iStep * numStepsRecip;
const float3 currPos = lerp(rayStartPos, rayEndPos, t);
const float t = iStep * raymarchInfo.numStepsRecip;
const float3 currPos = lerp(ray.startPos, ray.endPos, t);
#ifdef CUTOUT_ON
if (IsCutout(currPos))
@ -329,7 +373,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;
}
@ -339,7 +383,7 @@
frag_out output;
output.colour = col;
#if DEPTHWRITE_ON
output.depth = localToDepth(lerp(rayStartPos, rayEndPos, (iStep * stepSize)) - float3(0.5f, 0.5f, 0.5f));
output.depth = localToDepth(lerp(ray.startPos, ray.endPos, (iStep * stepSize)) - float3(0.5f, 0.5f, 0.5f));
#endif
return output;
}