Accellerate camera draw with GPU

pull/53/head
James Ball 2022-03-14 23:40:33 +00:00
rodzic 2be2006667
commit 136faea4f1
11 zmienionych plików z 191 dodań i 57 usunięć

Wyświetl plik

@ -165,6 +165,11 @@
<artifactId>java-data-front</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.aparapi</groupId>
<artifactId>aparapi</artifactId>
<version>3.0.0</version>
</dependency>
</dependencies>
<profiles>

Wyświetl plik

@ -1,8 +1,5 @@
package sh.ball.engine;
import java.util.HashMap;
import java.util.Map;
import sh.ball.shapes.Line;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
@ -23,6 +20,8 @@ public class Camera {
private static final int SAMPLE_RENDER_SAMPLES = 50;
private static final int VERTEX_SAMPLES = 1000;
public final CameraDrawKernel kernel = new CameraDrawKernel();
private double focalLength;
private Vector3 pos;
@ -36,7 +35,7 @@ public class Camera {
}
public List<Shape> draw(WorldObject worldObject) {
return getFrame(worldObject.getVertexPath());
return kernel.draw(this, worldObject);
}
// Automatically finds the correct Z position to use to view the world object properly.
@ -94,24 +93,11 @@ public class Camera {
double focal = focalLength;
return new Vector2(
vertex.getX() * focal / (vertex.getZ() - pos.getZ()) + pos.getX(),
vertex.getY() * focal / (vertex.getZ() - pos.getZ()) + pos.getY()
vertex.x * focal / (vertex.z - pos.z) + pos.x,
vertex.y * focal / (vertex.z - pos.z) + pos.y
);
}
public List<Shape> getFrame(List<Vector3> vertexPath) {
List<Shape> lines = new ArrayList<>();
for (int i = 0; i < vertexPath.size(); i += 2) {
lines.add(new Line(
project(vertexPath.get(i)),
project(vertexPath.get(i + 1))
));
}
return lines;
}
public void setFocalLength(double focalLength) {
this.focalLength = focalLength;
}

Wyświetl plik

@ -0,0 +1,127 @@
package sh.ball.engine;
import com.aparapi.Kernel;
import com.aparapi.Range;
import com.aparapi.exception.QueryFailedException;
import sh.ball.shapes.Line;
import sh.ball.shapes.Shape;
import sh.ball.shapes.Vector2;
import java.util.Arrays;
import java.util.List;
public class CameraDrawKernel extends Kernel {
private WorldObject prevObject = null;
private float[] vertices;
private float[] vertexResult;
private Shape[] lines;
private float rotationX;
private float rotationY;
private float rotationZ;
private float positionX;
private float positionY;
private float positionZ;
private float cameraPosX;
private float cameraPosY;
private float cameraPosZ;
private float focalLength;
public CameraDrawKernel() {}
public List<Shape> draw(Camera camera, WorldObject object) {
if (prevObject != object) {
List<Vector3> vertices = object.getVertexPath();
this.vertices = new float[vertices.size() * 3];
this.vertexResult = new float[vertices.size() * 2];
this.lines = new Line[vertices.size() / 2];
for (int i = 0; i < vertices.size(); i++) {
Vector3 vertex = vertices.get(i);
this.vertices[3 * i] = (float) vertex.x;
this.vertices[3 * i + 1] = (float) vertex.y;
this.vertices[3 * i + 2] = (float) vertex.z;
}
}
prevObject = object;
Vector3 rotation = object.getRotation();
Vector3 position = object.getPosition();
this.rotationX = (float) rotation.x;
this.rotationY = (float) rotation.y;
this.rotationZ = (float) rotation.z;
this.positionX = (float) position.x;
this.positionY = (float) position.y;
this.positionZ = (float) position.z;
Vector3 cameraPos = camera.getPos();
this.cameraPosX = (float) cameraPos.x;
this.cameraPosY = (float) cameraPos.y;
this.cameraPosZ = (float) cameraPos.z;
this.focalLength = (float) camera.getFocalLength();
int maxGroupSize = 256;
try {
maxGroupSize = getKernelMaxWorkGroupSize(getTargetDevice());
} catch (QueryFailedException e) {
e.printStackTrace();
}
execute(Range.create(roundUp(vertices.length / 3, maxGroupSize), maxGroupSize));
for (int i = 0; i < vertices.length / 3; i += 2) {
lines[i / 2] = new Line(
new Vector2(vertexResult[2 * i], vertexResult[2 * i + 1]),
new Vector2(vertexResult[2 * i + 2], vertexResult[2 * i + 3])
);
}
return Arrays.asList(lines);
}
int roundUp(int round, int multiple) {
if (multiple == 0) {
return round;
}
int remainder = round % multiple;
if (remainder == 0) {
return round;
}
return round + multiple - remainder;
}
@Override
public void run() {
int i = getGlobalId();
// get vertex
float x = vertices[3 * i];
float y = vertices[3 * i + 1];
float z = vertices[3 * i + 2];
// rotate around x-axis
float cosValue = cos(rotationX);
float sinValue = sin(rotationX);
float y2 = cosValue * y - sinValue * z;
float z2 = sinValue * y + cosValue * z;
// rotate around y-axis
cosValue = cos(rotationY);
sinValue = sin(rotationY);
float x2 = cosValue * x + sinValue * z2;
float z3 = -sinValue * x + cosValue * z2;
// rotate around z-axis
cosValue = cos(rotationZ);
sinValue = sin(rotationZ);
float x3 = cosValue * x2 - sinValue * y2;
float y3 = sinValue * x2 + cosValue * y2;
// add position
x = x3 + positionX;
y = y3 + positionY;
z = z3 + positionZ;
// projection
vertexResult[2 * i] = x * focalLength / (z - cameraPosZ) + cameraPosX;
vertexResult[2 * i + 1] = y * focalLength / (z - cameraPosZ) + cameraPosY;
}
}

Wyświetl plik

@ -5,7 +5,7 @@ import java.util.Objects;
public final class Vector3 {
private final double x, y, z;
public final double x, y, z;
public Vector3(double x, double y, double z) {
this.x = x;
@ -21,18 +21,6 @@ public final class Vector3 {
this(0, 0, 0);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public double getZ() {
return z;
}
public Vector3 add(Vector3 other) {
return new Vector3(
x + other.x,

Wyświetl plik

@ -44,13 +44,7 @@ public class WorldObject {
}
public List<Vector3> getVertexPath() {
List<Vector3> newVertices = new ArrayList<>();
for (Vector3 vertex : vertexPath) {
newVertices.add(vertex.rotate(rotation).add(position));
}
return newVertices;
return vertexPath;
}
public void getDrawPath(Set<Line3D> edges) {
@ -89,6 +83,10 @@ public class WorldObject {
}
}
public Vector3 getRotation() {
return rotation;
}
public void setRotation(Vector3 rotation) {
this.rotation = rotation;
}
@ -105,6 +103,10 @@ public class WorldObject {
position = position.add(translation);
}
public Vector3 getPosition() {
return position;
}
public void resetPosition() {
position = new Vector3();
}

Wyświetl plik

@ -124,11 +124,11 @@ public class ObjController implements Initializable, SubController {
public Element save(Document document) {
Element element = document.createElement("objectRotation");
Element x = document.createElement("x");
x.appendChild(document.createTextNode(Double.toString(rotation.getX())));
x.appendChild(document.createTextNode(Double.toString(rotation.x)));
Element y = document.createElement("y");
y.appendChild(document.createTextNode(Double.toString(rotation.getY())));
y.appendChild(document.createTextNode(Double.toString(rotation.y)));
Element z = document.createElement("z");
z.appendChild(document.createTextNode(Double.toString(rotation.getZ())));
z.appendChild(document.createTextNode(Double.toString(rotation.z)));
element.appendChild(x);
element.appendChild(y);
element.appendChild(z);

Wyświetl plik

@ -7,12 +7,13 @@ public class CubicBezierCurve extends Shape {
private final Vector2 p2;
private final Vector2 p3;
private double length = INVALID_LENGTH;
public CubicBezierCurve(Vector2 p0, Vector2 p1, Vector2 p2, Vector2 p3) {
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.length = new Line(p0, p3).length;
}
@Override
@ -45,4 +46,12 @@ public class CubicBezierCurve extends Shape {
return new CubicBezierCurve(p0.translate(vector), p1.translate(vector), p2.translate(vector),
p3.translate(vector));
}
@Override
public double getLength() {
if (length == INVALID_LENGTH) {
length = new Line(p0, p3).getLength();
}
return length;
}
}

Wyświetl plik

@ -7,13 +7,13 @@ public final class Ellipse extends Shape {
private final double rotation;
private final Vector2 position;
private double length = INVALID_LENGTH;
public Ellipse(double a, double b, double rotation, Vector2 position) {
this.a = a;
this.b = b;
this.rotation = rotation;
this.position = position;
// Approximation of length.
this.length = 2 * Math.PI * Math.sqrt((a * a + b * b) / 2);
}
public Ellipse(double a, double b, Vector2 position) {
@ -57,4 +57,13 @@ public final class Ellipse extends Shape {
public Ellipse translate(Vector2 vector) {
return new Ellipse(a, b, rotation, position.translate(vector));
}
@Override
public double getLength() {
if (length == INVALID_LENGTH) {
// Approximation of length.
length = 2 * Math.PI * Math.sqrt((a * a + b * b) / 2);
}
return length;
}
}

Wyświetl plik

@ -8,23 +8,17 @@ public final class Line extends Shape {
private final Vector2 a;
private final Vector2 b;
private double length = INVALID_LENGTH;
public Line(Vector2 a, Vector2 b) {
this.a = a;
this.b = b;
this.length = calculateLength();
}
public Line(double x1, double y1, double x2, double y2) {
this(new Vector2(x1, y1), new Vector2(x2, y2));
}
private double calculateLength() {
double ac = Math.abs(getY2() - getY1());
double cb = Math.abs(getX2() - getX1());
return Math.hypot(ac, cb);
}
@Override
public Line rotate(double theta) {
return new Line(a.rotate(theta), b.rotate(theta));
@ -35,6 +29,17 @@ public final class Line extends Shape {
return new Line(a.translate(vector), b.translate(vector));
}
@Override
public double getLength() {
if (length == INVALID_LENGTH) {
double ac = Math.abs(getY2() - getY1());
double cb = Math.abs(getX2() - getX1());
length = Math.hypot(ac, cb);
}
return length;
}
@Override
public Line scale(double factor) {
return new Line(a.scale(factor), b.scale(factor));

Wyświetl plik

@ -8,7 +8,7 @@ import java.util.function.Function;
public abstract class Shape {
protected double length;
protected static final double INVALID_LENGTH = -1;
public abstract Vector2 nextVector(double drawingProgress);
@ -20,9 +20,7 @@ public abstract class Shape {
public abstract Shape translate(Vector2 vector);
public double getLength() {
return length;
}
public abstract double getLength();
/* SHAPE HELPER FUNCTIONS */

Wyświetl plik

@ -82,6 +82,11 @@ public final class Vector2 extends Shape {
return new Vector2(getX() + vector.getX(), getY() + vector.getY());
}
@Override
public double getLength() {
return 0;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {