Đường viền kép - Tìm điểm đặc trưng, ​​tắt thông thường


9

Tôi đang làm theo hướng dẫn này để triển khai Đường viền kép http://www.sandboxie.com/misc/isosurf/isosurfaces.html

Nguồn dữ liệu của tôi là một lưới 16x16x16; Tôi đi qua lưới này từ dưới lên trên, từ trái sang phải, gần xa.

Đối với mỗi chỉ mục của lưới của tôi, tôi tạo một cấu trúc khối:

public Cube(int x, int y, int z, Func<int, int, int, IsoData> d, float isoLevel) {
            this.pos = new Vector3(x,y,z);
            //only create vertices need for edges
            Vector3[] v = new Vector3[4];
            v[0] = new Vector3 (x + 1, y + 1, z);
            v[1] = new Vector3 (x + 1, y, z + 1);
            v[2] = new Vector3 (x + 1, y + 1, z + 1);
            v[3] = new Vector3 (x, y + 1, z + 1);
            //create edges from vertices
            this.edges = new Edge[3];
            edges[0] = new Edge (v[1], v[2], d, isoLevel);
            edges[1] = new Edge (v[2], v[3], d, isoLevel);
            edges[2] = new Edge (v[0], v[2], d, isoLevel);
        }

Do cách tôi đi qua lưới, tôi chỉ cần nhìn vào 4 đỉnh và 3 cạnh. Trong ảnh này, các đỉnh 2, 5, 6, 7 tương ứng với các đỉnh 0, 1, 2, 3 và các cạnh 5, 6, 10 tương ứng với các cạnh 0, 1, 2 của tôi. Lưới khối

Một cạnh trông như thế này:

    public Edge(Vector3 p0, Vector3 p1, Func<int, int, int, IsoData> d, float isoLevel) {
        //get density values for edge vertices, save in vector , d = density function, data.z = isolevel 
        this.data = new Vector3(d ((int)p0.x, (int)p0.y, (int)p0.z).Value, d ((int)p1.x, (int)p1.y, (int)p1.z).Value, isoLevel);
        //get intersection point
        this.mid = LerpByDensity(p0,p1,data);
        //calculate normals by gradient of surface
        Vector3 n0 = new Vector3(d((int)(p0.x+1),   (int)p0.y,      (int)p0.z       ).Value - data.x,
                                 d((int)p0.x,       (int)(p0.y+1),  (int)p0.z       ).Value - data.x,
                                 d((int)p0.x,       (int)p0.y,      (int)(p0.z+1)   ).Value - data.x);

        Vector3 n1 = new Vector3(d((int)(p1.x+1),   (int)p1.y,      (int)p1.z       ).Value - data.y,
                                 d((int)p1.x,       (int)(p1.y+1),  (int)p1.z       ).Value - data.y,
                                 d((int)p1.x,       (int)p1.y,      (int)(p1.z+1)   ).Value - data.y);
        //calculate normal by averaging normal of edge vertices
        this.normal = LerpByDensity(n0,n1,data);
    }

Sau đó tôi kiểm tra tất cả các cạnh để thay đổi dấu hiệu, nếu có một cái tôi tìm thấy các hình khối xung quanh và lấy điểm đặc trưng của các hình khối đó.

Bây giờ nó hoạt động nếu tôi đặt điểm đặc trưng vào trung tâm khối, sau đó tôi có được giao diện minecraft khối. Nhưng đó không phải là điều tôi muốn.

Để tìm điểm đặc trưng, ​​tôi muốn làm như trong bài đăng này: https://gamedev.stackexchange.com/a/83757/49583

Về cơ bản, bạn bắt đầu đỉnh ở trung tâm của tế bào. Sau đó, bạn lấy trung bình tất cả các vectơ được lấy từ đỉnh đến mỗi mặt phẳng và di chuyển đỉnh dọc theo kết quả đó và lặp lại bước này một số lần cố định. Tôi thấy việc di chuyển nó ~ 70% dọc theo kết quả sẽ ổn định với số lần lặp ít nhất.

Vì vậy, tôi đã có một lớp Máy bay:

private class Plane {

        public Vector3 normal;
        public float distance;

        public Plane(Vector3 point, Vector3 normal) {
            this.normal = Vector3.Normalize(normal);
            this.distance = -Vector3.Dot(normal,point);
        }

        public float Distance(Vector3 point) {
            return Vector3.Dot(this.normal, point) + this.distance;
        }

        public Vector3 ShortestDistanceVector(Vector3 point) {
            return this.normal * Distance(point);
        }
 }

và một chức năng để có được điểm đặc trưng, ​​trong đó tôi tạo 3 mặt phẳng, một mặt phẳng cho mỗi cạnh và tính trung bình khoảng cách đến tâm:

 public Vector3 FeaturePoint {
            get {
                Vector3 c = Center;
 //                 return c; //minecraft style

                Plane p0 = new Plane(edges[0].mid,edges[0].normal);
                Plane p1 = new Plane(edges[1].mid,edges[1].normal);
                Plane p2 = new Plane(edges[2].mid,edges[2].normal);

                int iterations = 5;
                for(int i = 0; i < iterations; i++) {
                    Vector3 v0 = p0.ShortestDistanceVector(c);
                    Vector3 v1 = p1.ShortestDistanceVector(c);
                    Vector3 v2 = p2.ShortestDistanceVector(c);
                    Vector3 avg = (v0+v1+v2)/3;
                    c += avg * 0.7f;
                }

                return c;
            }
        }

Nhưng nó không hoạt động, các đỉnh ở khắp mọi nơi. Lỗi ở đâu? Tôi thực sự có thể tính cạnh bình thường bằng cách lấy trung bình bình thường của các đỉnh cạnh không? Tôi không thể có được mật độ tại điểm giữa cạnh, vì tôi chỉ có một lưới số nguyên làm nguồn dữ liệu ...

Chỉnh sửa: Tôi cũng tìm thấy ở đây http://www.mathsisfun.com/acheebra/systems-linear-equations-matrices.html rằng tôi có thể sử dụng ma trận để tính toán giao điểm của 3 mặt phẳng, ít nhất đó là cách tôi hiểu nó, vì vậy Tôi đã tạo ra phương pháp này

 public static Vector3 GetIntersection(Plane p0, Plane p1, Plane p2) {              
            Vector3 b = new Vector3(-p0.distance, -p1.distance, -p2.distance);

            Matrix4x4 A = new Matrix4x4 ();
            A.SetRow (0, new Vector4 (p0.normal.x, p0.normal.y, p0.normal.z, 0));
            A.SetRow (1, new Vector4 (p1.normal.x, p1.normal.y, p1.normal.z, 0));
            A.SetRow (2, new Vector4 (p2.normal.x, p2.normal.y, p2.normal.z, 0));
            A.SetRow (3, new Vector4 (0, 0, 0, 1));

            Matrix4x4 Ainv = Matrix4x4.Inverse(A);

            Vector3 result = Ainv * b;
            return result;
        }

mà với dữ liệu này

        Plane p0 = new Plane (new Vector3 (2, 0, 0), new Vector3 (1, 0, 0));
        Plane p1 = new Plane (new Vector3 (0, 2, 0), new Vector3 (0, 1, 0));
        Plane p2 = new Plane (new Vector3 (0, 0, 2), new Vector3 (0, 0, 1));

        Vector3 cq = Plane.GetIntersection (p0, p1, p2);

tính toán một giao điểm tại (2.0, 2.0, 2.0), vì vậy tôi giả sử nó hoạt động chính xác. Tuy nhiên, không phải là đỉnh chính xác. Tôi thực sự nghĩ rằng đó là quy tắc của tôi.


Unity đã có một Planecấu trúc được xác định ( xem tại đây ), có các phương thức bạn đã xác định (ngoại trừ phương thức vectơ ngắn nhất, bạn có thể thêm vào Planecấu trúc bằng các phương thức mở rộng C #). Bạn có thể sử dụng GetDistanceToPointphương pháp thay vì phương pháp của bạn Distance.
EvilTak

Cảm ơn nhận xét của bạn, tôi đã thay thế triển khai của mình bằng triển khai Unity và sử dụng chức năng này riêng tư Vector3 shortestDistanceVector (Mặt phẳng p, điểm Vector3) {return p.GetDistanceToPoint (point) * p.n normal; } Tôi cũng chỉ nhận được các đỉnh ngẫu nhiên. Tôi nghi ngờ thông thường của tôi là hoàn toàn tắt. Tôi cũng đã thêm một chỉnh sửa, trong đó tôi đã thử một phương pháp thứ hai, có lẽ bạn có thể xem nó và cho tôi biết tôi đã làm gì sai ở đó.
ElDuderino

2
Can I actually calculate the edge normal by averaging the normal of the edge vertices?- Tôi có thể nhầm, nhưng tôi nghĩ rằng tôi đã thấy lời khuyên ở nơi khác nói rằng đừng bao giờ nội suy để có được quy tắc - họ chỉ không nội suy tốt. Tính trên mỗi khuôn mặt, nó an toàn hơn. Thực sự, trước tiên bạn nên xây dựng một trường hợp thử nghiệm tối thiểu để đảm bảo tính toán chuẩn của bạn là chính xác. Sau đó tiếp tục với điều này.
Kỹ sư

Nhưng tôi chỉ nhận được các mặt sau khi tôi có các quy tắc, tôi cần các quy tắc để tạo các mặt phẳng và lấy các đỉnh cho các mặt từ chúng. Và như đã nói với cấu trúc hiện tại của tôi, tôi chỉ có thể lập chỉ mục vào dữ liệu của mình ở các đỉnh cạnh. Hay những khuôn mặt bạn đang nói về?
ElDuderino

@ElDuderino Khuôn mặt giống như khuôn mặt (hoặc hình tam giác) của lưới, nhưng tôi không biết làm thế nào bạn có thể lấy được điều đó từ dữ liệu của mình. Nếu bạn có thể tạo ra các hình tam giác thay vì các cạnh, thì phép tính bình thường trở nên thực sự dễ dàng.
EvilTak

Câu trả lời:


1

Trước hết, các quy tắc thông thường của bạn sẽ hoàn toàn ổn nếu chúng được tính toán thông qua các khác biệt về phía trước / phía sau- / trung tâm. Vấn đề là, bạn đã di chuyển điểm trung tâm của mình theo hướng sai trong chức năng FeaturePoint, điều này dẫn đến việc đi xa hơn mức tối thiểu.

Vector3 c = Center;
Plane p0 = new Plane(edges[0].mid,edges[0].normal);
Plane p1 = new Plane(edges[1].mid,edges[1].normal);
Plane p2 = new Plane(edges[2].mid,edges[2].normal);

int iterations = 5;
for(int i = 0; i < iterations; i++) {
    Vector3 v0 = p0.GetDistanceToPoint(c) * edges[0].normal;
    Vector3 v1 = p1.GetDistanceToPoint(c) * edges[1].normal;
    Vector3 v2 = p2.GetDistanceToPoint(c) * edges[2].normal;
    Vector3 avg = (v0+v1+v2)/3;
    c -= avg * 0.7f; // Error was here!
}
return c;

Điều này rất vui vì mã của bạn không hội tụ vào một điểm và do đó nhảy ra khỏi hộp voxel của bạn. Tôi không biết mã từ Ai đó có thể giải thích đường viền kép không? được dự định sử dụng phương pháp chiếu trong đó điểm được chiếu lên mặt phẳng qua:

distance = Vector3.Dot(point - origin, normal);
projectedPoint = point - distance * normal;

Nhưng đó là cùng một phương pháp. Nếu bạn viết lại phép chiếu vào mã gốc của mình, điều này sẽ dẫn đến:

    Vector3 v0 = c - p0.GetDistanceToPoint(c) * edges[0].normal;
    Vector3 v1 = c - p1.GetDistanceToPoint(c) * edges[1].normal;
    Vector3 v2 = c - p2.GetDistanceToPoint(c) * edges[2].normal;
    c = (v0+v1+v2)/3;

có thể được viết lại thành:

    Vector3 v0 = p0.GetDistanceToPoint(c) * edges[0].normal;
    Vector3 v1 = p1.GetDistanceToPoint(c) * edges[1].normal;
    Vector3 v2 = p2.GetDistanceToPoint(c) * edges[2].normal;
    c = c - (v0+v1+v2)/3;

và do đó dẫn đến mã đầu tiên. Bằng cách chiếu điểm trên ba mặt phẳng không phẳng, nó từ từ hội tụ về mức tối thiểu vì bạn thu nhỏ khoảng cách từ mỗi mặt phẳng đến điểm của bạn như trong hình.

Các chấm màu đỏ biểu thị điểm đặc trưng, ​​các đường màu xanh là các quy tắc và điểm màu tím mà điểm được chiếu trên mặt phẳng. Bạn cũng không cần sử dụng hệ số 0,7 vì nó sẽ hội tụ nhanh hơn nếu không có nó. Nếu bạn sử dụng phương pháp này hãy cẩn thận, thuật toán có thể không hoạt động nếu bạn có các mặt phẳng không giao nhau.


Này, thật tuyệt khi nhận được câu trả lời sau 2 năm :) Tôi chưa bao giờ tìm ra giải pháp, vì vậy tôi đã dừng dự án này, nhưng tôi sẽ xem lại nó với kiến ​​thức này và cho bạn biết nó đã diễn ra như thế nào. Có 1 til rồi.
ElDuderino

Tuyệt vời! Tôi rất vui vì tôi có thể giúp bạn. Hãy cho tôi biết nếu nó làm việc cho bạn.
Tim Rolff
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.