Ánh xạ kết cấu chính xác


7

Tôi đang làm việc trên một công cụ kết xuất nhỏ cho một dự án cá nhân và tôi gặp vấn đề với phần ánh xạ kết cấu của nó.

Nó dường như làm việc cho một số trường hợp, nhưng không phải cho những người khác. Ví dụ: khi một trong các đỉnh nằm phía sau máy ảnh, kết cấu sẽ bị kéo dài.

Dường như đúng trường hợpCó vẻ đúng trường hợp Trường hợp không chính xác

Tôi đoán rằng nó có một cái gì đó để làm với ánh xạ kết cấu không chính xác. Tôi đã thử nhiều thay đổi chủ yếu liên quan đến khoảng cách z đến máy ảnh, nhưng tôi không thể tìm thấy bất kỳ sửa chữa nhanh nào cho mã của mình.

Đây là mã của tôi để chiếu phối cảnh:

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 };
}

và ở đây mã của tôi cho ánh xạ kết cấu:

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 };
}

và đây là mã bản vẽ quad của tôi:

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);
        }
}

Bất cứ ai có thể chỉ ra những gì tôi đang làm sai? Tôi không có nhiều kinh nghiệm vì đây là lần đầu tiên tôi thử triển khai một công cụ trò chơi.

Cảm ơn bạn cho bất kỳ cái nhìn sâu sắc.


Bạn có thể có một thời gian tốt hơn để đặt câu hỏi này trên computergraphics.stackexchange.com
Xà phòng


@Soacco Cảm ơn, tôi sẽ đăng ở đó sau thời gian hồi chiêu.
gọn gàng

@Syntac_ Tôi đã xem trang này, nhưng tôi không thể tìm ra giải pháp cho vấn đề của mình.
gọn gàng

Mã của bạn rất cần ý kiến.
sam hocevar

Câu trả lời:


1

Mã của bạn không sai chút nào. Bạn đang xử lý ma trận và đỉnh đúng cách (mô phỏng 3D của bạn thực sự hoạt động) và thậm chí ánh xạ kết cấu cũng ổn. Vấn đề là, thuật toán ánh xạ kết cấu của bạn chỉ sử dụng phép nội suy tuyến tính để ánh xạ một điểm trong không gian 3D đến một điểm trong sơ đồ 2D của một kết cấu nhất định.

Ánh xạ họa tiết
Khi ánh xạ kết cấu tới đa giác lưới, bạn thường xem xét 3 ple trong không gian ba chiều (ba đỉnh 3D) và liên kết nó với 3 ple khác trong sơ đồ 2D (ba đỉnh 2D trong một kết cấu). Khi vẽ, ba đỉnh 3D được chiếu trên màn hình ( [x, y, z] thành [x ', y'] ); sau đó hình tam giác được xem xét trên kết cấu (ba đỉnh 2D của chúng ta) được vẽ trên màn hình, cuối cùng biến đổi các đỉnh 2D để phù hợp với hình chiếu trước đó. Bạn biết điều này bởi vì bạn thực sự đã làm nó.
Nhưng , thuật toán ánh xạ kết cấu mà bạn triển khai là ánh xạ kết cấu affine , nghĩa là các tam giác được vẽ chỉ bằng các phép biến đổi affine mặt phẳng(do đó, dịch, xoay, chia tỷ lệ, lật / phản chiếu), vì vậy khi vẽ bạn đang xem xét tọa độ như hiện tại, không có thông tin về độ sâu thực tế của chúng trong các đối tác ba chiều của chúng. Bạn chỉ đang vẽ họa tiết.

Chỉnh sửa phối cảnh Để vẽ họa tiết chính xác trong phép chiếu không gian 3D, chúng ta cần xem xét các đỉnh cùng với một số thông tin về độ sâu của chúng trong không gian liên quan đến vị trí camera 3D. Để thực hiện hiệu chỉnh phối cảnh cho kết cấu, chúng tôi xem xét công thức nội suy tuyến tính:

nhập mô tả hình ảnh ở đây, Nơi nhập mô tả hình ảnh ở đây.

Ánh xạ kết cấu affine trực tiếp nội suy giữa hai giá trị và việc ghép hai lần cho phép động cơ vẽ mọi texel cần thiết. Đây là một phép tính nhanh để thực hiện, nhưng kết quả không thực tế.

Bằng cách giới thiệu thông tin về độ sâu, chúng tôi đang nói với công cụ rằng hình tam giác của chúng tôi không phải là hình phẳng trên màn hình và điều này đạt được bằng cách chỉnh sửa công thức nội suy như sau:

nhập mô tả hình ảnh ở đây

Trong đó z iđộ sâu của đỉnh i trong phép nội suy. Bằng cách này, khi di chuyển dọc theo kết cấu, texel được tính toán sẽ ở một vị trí khác so với lerp , phản ánh hình chiếu phối cảnh thực với độ trung thực hơn nhiều. Thông thường tính toán này là khó khăn hơn để thực hiện hơn lerping , và do tính phức tạp này một GPU chuyên dụng là một lợi thế (và được tối ưu hóa) để tọa độ texture tính toán. Để CPU thực hiện tất cả các công việc có nghĩa là buộc xử lý đỉnh và không tốt cho hiệu suất trò chơi của bạn.

Đây là so sánh giữa kết cấu ban đầu (bàn cờ), ánh xạ kết cấu affine của nó và ánh xạ kết cấu chính xác.

nhập mô tả hình ảnh ở đây

Vẽ từ kết cấu đến màn hình được thực hiện bằng các kỹ thuật phân chia màn hình phụ. Đây là các phương pháp để phân chia các khu vực đó trên màn hình được chỉ định để vẽ họa tiết thành từng mảnh trước khi thực hiện vẽ kết cấu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.