它似乎在某些情况下有效,但不适用于其他情况。例如,当顶点之一在摄影机后面时,纹理将被拉伸。
看似正确的大小写
错误的大小写
我猜想这与纹理映射不正确有关。我已经尝试了各种更改,主要涉及到相机的z距离,但是我找不到对代码的任何快速修复。
这是透视投影的代码:
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 };
}
这是我的纹理映射代码:
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 };
}
这是我的四边形绘制代码:
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);
}
}
谁能指出我在做什么错?我不太有经验,因为这是我第一次尝试实现游戏引擎。
感谢您提供任何见识。
评论
我没有读过您的代码,因为我看到了这一点:“例如,当其中一个顶点位于摄影机后面时,纹理将被拉伸。”您必须将多边形裁剪到相机前面的Z =恒定平面上,否则会发生各种混乱:-)@SimonF我已经在Z轴上进行剪切,请参见第3个代码中的ZCLIP。
我很抱歉!我看到了开头的段落,它“升起了红旗”。因此,考虑到“我猜想这与纹理映射不正确有关”。 ..我试图了解您的“地图”功能,但迷路了。例如,我不明白为什么需要平方根。 IIRC有一个从三角坐标开始的初始设置,以创建9个参数,a,b,cdefpqr,然后@像素x,y,U =(ax + by + c)/(px + qy + r)和V =(dx + ey + f)/(px + qy + r)。要确定abc..pqr,需要使用简化的“ 3x3”矩阵(即反w / out det)和矩阵muls的伴随。
@SimonF我的map()函数根据此StackoverFlow答案改编而成。我以为是透视正确的,但我猜不是。是否所有透视正确的纹理贴图技术都需要将多边形分解为三角形?是否有不是矩阵形式的公式?谢谢!
人们到底为什么要避免矩阵计算。三角形使转换坐标变得容易。