Ich arbeite an einer kleinen Rendering-Engine für ein persönliches Projekt und habe Probleme mit dem Textur-Mapping-Teil.
Es scheint in einigen Fällen zu funktionieren, in anderen jedoch nicht. Wenn sich beispielsweise einer der Scheitelpunkte hinter der Kamera befindet, wird die Textur gedehnt.
Scheinbar korrekter Fall Falscher Fall
Ich vermute, dass es etwas damit zu tun hat, dass das Textur-Mapping nicht perspektivisch korrekt ist. Ich habe verschiedene Änderungen versucht, die hauptsächlich den Z-Abstand zur Kamera betreffen, konnte jedoch keine schnelle Lösung für meinen Code finden.
Hier ist mein Code für die perspektivische Projektion:
public double[] project(double x, double y, double z) {
double tx = x - camera.x;
double ty = z - camera.z;
double tz = y - camera.y;
double cx = Math.cos(camera.pitch);
double cy = Math.cos(camera.yaw);
double cz = Math.cos(camera.roll);
double sx = Math.sin(camera.pitch);
double sy = Math.sin(camera.yaw);
double sz = Math.sin(camera.roll);
double dx = cy * (sz * ty + cz * tx) - sy * tz;
double dy = sx * (cy * tz + sy * (sz * ty + cz * tx)) + cx * (cz * ty - sz * tx);
double dz = cx * (cy * tz + sy * (sz * ty + cz * tx)) - sx * (cz * ty - sz * tx);
double ez = 1.0 / Math.tan(FOV / 2.0);
double bx = ez / dz * dx;
double by = ez / dz * dy;
if (dz < 0.0) {
bx = -bx;
by = -by;
}
int px = (int) (width + bx * height) / 2;
int py = (int) (height + by * height) / 2;
return new double[] { px, py, dz };
}
und hier mein Code für das Textur-Mapping:
public double[] map(double x, double y, double x0, double y0, double x1, double y1, double x2, double y2, double x3, double y3) {
double A = (x0 - x) * (y0 - y2) - (y0 - y) * (x0 - x2);
double B = ((x0 - x) * (y1 - y3) - (y0 - y) * (x1 - x3) + (x1 - x) * (y0 - y2) - (y1 - y) * (x0 - x2)) / 2.0;
double C = (x1 - x) * (y1 - y3) - (y1 - y) * (x1 - x3);
double det = A - 2.0 * B + C;
double u;
if (det == 0.0) {
u = A / (A - C);
if (Double.isNaN(u) || u < 0.0 || u > 1.0)
return null;
} else {
double u1 = ((A - B) + Math.sqrt(B * B - A * C)) / det;
boolean u1valid = !Double.isNaN(u1) && u1 >= 0.0 && 1.0 >= u1;
double u2 = ((A - B) - Math.sqrt(B * B - A * C)) / det;
boolean u2valid = !Double.isNaN(u2) && u2 >= 0.0 && 1.0 >= u2;
if (u1valid && u2valid)
u = u1 < u2 ? u2 : u1;
else if (u1valid)
u = u1;
else if (u2valid)
u = u2;
else
return null;
}
double v1 = ((1.0 - u) * (x0 - x) + u * (x1 - x)) / ((1.0 - u) * (x0 - x2) + u * (x1 - x3));
boolean v1valid = !Double.isNaN(v1) && v1 >= 0.0 && 1.0 >= v1;
double v2 = ((1.0 - u) * (y0 - y) + u * (y1 - y)) / ((1.0 - u) * (y0 - y2) + u * (y1 - y3));
boolean v2valid = !Double.isNaN(v2) && v2 >= 0.0 && 1.0 >= v2;
double v;
if (v1valid && v2valid)
v = v1 < v2 ? v2 : v1;
else if (v1valid)
v = v1;
else if (v2valid)
v = v2;
else
return null;
return new double[] { u, v };
}
und hier ist mein Quad-Zeichnungscode:
public void renderFace(Screen screen, int x0, int y0, int z0, int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
boolean render = true;
double[] p0 = screen.project(x0, y0, z0);
int px0 = (int) p0[0], py0 = (int) p0[1];
render |= p0[2] >= ZCLIP && px0 >= 0 && px0 < screen.width && py0 >= 0 && py0 < screen.height;
double[] p1 = screen.project(x1, y1, z1);
int px1 = (int) p1[0], py1 = (int) p1[1];
render |= p1[2] >= ZCLIP && px1 >= 0 && px1 < screen.width && py1 >= 0 && py1 < screen.height;
double[] p2 = screen.project(x2, y2, z2);
int px2 = (int) p2[0], py2 = (int) p2[1];
render |= p2[2] >= ZCLIP && px2 >= 0 && px2 < screen.width && py2 >= 0 && py2 < screen.height;
double[] p3 = screen.project(x3, y3, z3);
int px3 = (int) p3[0], py3 = (int) p3[1];
render |= p3[2] >= ZCLIP && px3 >= 0 && px3 < screen.width && py3 >= 0 && py3 < screen.height;
if (!render)
return;
int minX = Math.min(Math.min(px0, px1), Math.min(px2, px3));
if (minX < 0)
minX = 0;
if (minX > screen.width)
minX = screen.width;
int minY = Math.min(Math.min(py0, py1), Math.min(py2, py3));
if (minY < 0)
minY = 0;
if (minY > screen.height)
minY = screen.height;
int maxX = Math.max(Math.max(px0, px1), Math.max(px2, px3));
if (maxX < 0)
maxX = 0;
if (maxX > screen.width)
maxX = screen.width;
int maxY = Math.max(Math.max(py0, py1), Math.max(py2, py3));
if (maxY < 0)
maxY = 0;
if (maxY > screen.height)
maxY = screen.height;
if (minX == maxX || minY == maxY)
return;
for (int py = minY; py < maxY; ++py)
for (int px = minX; px < maxX; ++px) {
double[] uv = screen.map(px + 0.5, py + 0.5, px0, py0, px1, py1, px2, py2, px3, py3);
if (uv == null)
continue;
double u = uv[0], v = uv[1];
double pz = (1 - u) * ((1 - v) * p0[2] + v * p2[2]) + u * ((1 - v) * p1[2] + v * p3[2]);
if (pz < ZCLIP)
continue;
int texX = 15 - Math.min(15, (int) (16 * u));
int texY = 15 - Math.min(15, (int) (16 * v));
screen.setPixel(px, py, pz, Art.WALLS.getPixel(texX, texY) * BRICKS);
}
}
Kann jemand darauf hinweisen, was ich falsch mache? Ich bin nicht sehr erfahren, da dies mein erster Versuch ist, eine Spiel-Engine zu implementieren.
Vielen Dank für jeden Einblick.