Skip to content

Commit

Permalink
Fix ray clipping in alpha pass for parallel projection. (#1508)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThatRedox authored Dec 3, 2022
1 parent c007e5b commit 94622f8
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

import java.util.Random;

import se.llbit.chunky.renderer.scene.Scene;
import se.llbit.math.Ray;
import se.llbit.math.Vector3;

/**
Expand Down Expand Up @@ -52,4 +54,31 @@ public ParallelProjector(double worldDiagonalSize, double fov) {
@Override public double getDefaultFoV() {
return worldDiagonalSize / 2;
}

public static void fixRay(Ray ray, Scene scene) {
// When in parallel projection, push the ray origin back so the
// ray start outside the octree to prevent ray spawning inside some blocks
int limit = (1 << scene.getWorldOctree().getDepth());
Vector3 o = ray.o;
Vector3 d = ray.d;
double t = 0;
// simplified intersection test with the 6 planes that form the bounding box of the octree
if(Math.abs(d.x) > Ray.EPSILON) {
t = Math.min(t, -o.x / d.x);
t = Math.min(t, (limit - o.x) / d.x);
}
if(Math.abs(d.y) > Ray.EPSILON) {
t = Math.min(t, -o.y / d.y);
t = Math.min(t, (limit - o.y) / d.y);
}
if(Math.abs(d.z) > Ray.EPSILON) {
t = Math.min(t, -o.z / d.z);
t = Math.min(t, (limit - o.z) / d.z);
}
// set the origin to the farthest intersection point behind
// In theory, we only would need to set it to the closest intersection point behind
// but this doesn't matter because the Octree.enterOctree function
// will do the same amount of math for the same result no matter what the exact point is
ray.o.scaleAdd(t, d);
}
}
77 changes: 22 additions & 55 deletions chunky/src/java/se/llbit/chunky/renderer/scene/Scene.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import se.llbit.chunky.renderer.postprocessing.PostProcessingFilter;
import se.llbit.chunky.renderer.postprocessing.PostProcessingFilters;
import se.llbit.chunky.renderer.postprocessing.PreviewFilter;
import se.llbit.chunky.renderer.projection.ParallelProjector;
import se.llbit.chunky.renderer.projection.ProjectionMode;
import se.llbit.chunky.renderer.renderdump.RenderDump;
import se.llbit.chunky.renderer.scene.biome.BiomeStructure;
Expand Down Expand Up @@ -683,30 +684,7 @@ public void rayTrace(RayTracer rayTracer, WorkerState state) {
state.ray.o.z -= origin.z;

if(camera.getProjectionMode() == ProjectionMode.PARALLEL) {
// When in parallel projection, push the ray origin back so the
// ray start outside the octree to prevent ray spawning inside some blocks
int limit = (1 << worldOctree.getDepth());
Vector3 o = state.ray.o;
Vector3 d = state.ray.d;
double t = 0;
// simplified intersection test with the 6 planes that form the bounding box of the octree
if(Math.abs(d.x) > Ray.EPSILON) {
t = Math.min(t, -o.x / d.x);
t = Math.min(t, (limit - o.x) / d.x);
}
if(Math.abs(d.y) > Ray.EPSILON) {
t = Math.min(t, -o.y / d.y);
t = Math.min(t, (limit - o.y) / d.y);
}
if(Math.abs(d.z) > Ray.EPSILON) {
t = Math.min(t, -o.z / d.z);
t = Math.min(t, (limit - o.z) / d.z);
}
// set the origin to the farthest intersection point behind
// In theory, we only would need to set it to the closest intersection point behind
// but this doesn't matter because the Octree.enterOctree function
// will do the same amount of math for the same result no matter what the exact point is
o.scaleAdd(t, d);
ParallelProjector.fixRay(state.ray, this);
}

rayTracer.trace(this, state);
Expand Down Expand Up @@ -2380,38 +2358,27 @@ public void computeAlpha(int x, int y, WorkerState state) {
double invHeight = 1.0 / height;

// Rotated grid supersampling.
double[][] offsets = new double[][] {
{ -3.0 / 8.0, 1.0 / 8.0 },
{ 1.0 / 8.0, 3.0 / 8.0 },
{ -1.0 / 8.0, -3.0 / 8.0 },
{ 3.0 / 8.0, -1.0 / 8.0 },
};

camera
.calcViewRay(ray, -halfWidth + (x - 3 / 8.0) * invHeight, -.5 + (y + 1 / 8.0) * invHeight);
ray.o.x -= origin.x;
ray.o.y -= origin.y;
ray.o.z -= origin.z;

double occlusion = PreviewRayTracer.skyOcclusion(this, state);

camera
.calcViewRay(ray, -halfWidth + (x + 1 / 8.0) * invHeight, -.5 + (y + 3 / 8.0) * invHeight);
ray.o.x -= origin.x;
ray.o.y -= origin.y;
ray.o.z -= origin.z;

occlusion += PreviewRayTracer.skyOcclusion(this, state);

camera
.calcViewRay(ray, -halfWidth + (x - 1 / 8.0) * invHeight, -.5 + (y - 3 / 8.0) * invHeight);
ray.o.x -= origin.x;
ray.o.y -= origin.y;
ray.o.z -= origin.z;

occlusion += PreviewRayTracer.skyOcclusion(this, state);

camera
.calcViewRay(ray, -halfWidth + (x + 3 / 8.0) * invHeight, -.5 + (y - 1 / 8.0) * invHeight);
ray.o.x -= origin.x;
ray.o.y -= origin.y;
ray.o.z -= origin.z;

occlusion += PreviewRayTracer.skyOcclusion(this, state);
double occlusion = 0.0;
for (double[] offset : offsets) {
camera.calcViewRay(ray,
-halfWidth + (x + offset[0]) * invHeight,
-0.5 + (y + offset[1]) * invHeight);
ray.o.x -= origin.x;
ray.o.y -= origin.y;
ray.o.z -= origin.z;

if (camera.getProjectionMode() == ProjectionMode.PARALLEL) {
ParallelProjector.fixRay(state.ray, this);
}
occlusion += PreviewRayTracer.skyOcclusion(this, state);
}

alphaChannel[y * width + x] = (byte) (255 * occlusion * 0.25 + 0.5);
}
Expand Down

0 comments on commit 94622f8

Please sign in to comment.