Çift Konturlama - Özellik noktasını bulma, normaller kapalı


9

Ben İkili Contouring uygulamak için bu öğretici takip ediyorum http://www.sandboxie.com/misc/isosurf/isosurfaces.html

Veri kaynağım bir ızgara 16x16x16; Bu ızgarayı aşağıdan yukarıya, soldan sağa, uzaklara doğru geçiyorum.

Izgaramın her dizini için bir küp yapısı oluşturuyorum:

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

Izgarayı nasıl geçtiğime göre, sadece 4 köşeye ve 3 kenara bakmam gerekiyor. Bu resimde, 2, 5, 6, 7 köşeleri 0, 1, 2, 3 köşelerime karşılık gelir ve 5, 6, 10 kenarları 0, 1, 2 köşelerime karşılık gelir. Izgara küpü

Bir kenar şöyle görünür:

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

Daha sonra bir işaret değişikliği için tüm kenarları kontrol ederim, eğer bir tane varsa çevreleyen küpleri bulur ve bu küplerin özellik noktasını alırım.

Özellik noktasını küp merkezine ayarlarsam işe yarar, sonra bloklu minecraft görünümünü elde ederim. Ama istediğim bu değil.

Özellik noktasını bulmak için, bu yazıda olduğu gibi yapmak istedim: https://gamedev.stackexchange.com/a/83757/49583

Temel olarak, tepe noktasını hücrenin ortasından başlatırsınız. Sonra, tepe noktasından alınan her vektörü her bir düzlemde ortalamalandırır ve tepe noktasını bu sonuç boyunca hareket ettirirsiniz ve bu adımı sabit sayıda tekrarlayın. Sonuçta ~% 70 hareket ettirmenin en az yineleme miktarında stabilize olacağını buldum.

Bir Plane sınıfı aldım:

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

ve her bir kenar için bir tane olmak ve merkeze olan uzaklığı ortalama olarak 3 düzlem oluşturduğum özellik noktasını almak için bir işlev:

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

Ama işe yaramıyor, köşeler her yerde. Hata nerede? Aslında kenar köşelerinin normalinin ortalamasını alarak kenarı normal olarak hesaplayabilir miyim? Veri kaynağı olarak sadece bir tamsayı kılavuzum olduğu için, kenar orta noktasında yoğunluğu alamıyorum ...

Düzenleme: Burada da http://www.mathsisfun.com/algebra/systems-linear-equations-matrices.html 3 düzlemin kesişimini hesaplamak için matrisler kullanabileceğimi buldum , en azından ben böyle anladım, yani Bu yöntemi yarattım

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

hangi bu verilerle

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

(2.0, 2.0, 2.0) 'da bir kavşak hesaplar, bu yüzden doğru çalıştığını varsayıyorum. Yine de, doğru köşeleri değil. Gerçekten benim normallerim olduğunu düşünüyorum.


Birlik zaten Planetanımlanmış yöntemlere sahip bir yapıya sahiptir ( buraya bakınız ) ( PlaneC # uzatma yöntemlerini kullanarak yapıya ekleyebileceğiniz en kısa vektör yöntemi hariç ). Sen kullanabilirsiniz GetDistanceToPointsenin yerine yöntemini Distanceyöntemiyle.
EvilTak

Yorumunuz için teşekkürler, uygulamamı Unity uygulamasıyla değiştirdim ve bu işlevi kullanarak özel Vector3 shortestDistanceVector (Düzlem p, Vector3 noktası) {return p.GetDistanceToPoint (point) * s.normal; } Sadece rastgele köşeler de elde ediyorum. Normalimin tamamen kapalı olduğundan şüpheleniyorum. Ayrıca ikinci bir yöntem denediğim bir düzenleme ekledim, belki ona bir göz atabilir ve orada neyi yanlış yaptığımı söyleyebilirsiniz.
ElDuderino

2
Can I actually calculate the edge normal by averaging the normal of the edge vertices?- Yanılıyor olabilirim, ama sanırım başka yerlerde normallere ulaşmak için asla enterpolasyon yapmamanızı söyleyen bir tavsiye gördüm - sadece iyi enterpolasyon yapmıyorlar. Yüz başına hesapla, daha güvenlidir. Gerçekten, normal hesaplamanızın doğru olduğundan emin olmak için önce minimum bir test senaryosu oluşturmalısınız. Sonra buna devam edin.
Mühendis

Ama sadece normalleri aldıktan sonra yüzleri alıyorum, uçakları oluşturmak ve onlardan yüzler için köşeleri almak için normallere ihtiyacım var. Mevcut yapımla dediğim gibi verilerime yalnızca kenar noktalarında indeksleyebilirim. Ya da hangi yüzlerden bahsediyorsun?
ElDuderino

@ElDuderino Bir kafesin yüzleri (veya üçgenleri) gibi görünüyor, ancak bunu verilerinizden nasıl alabileceğinizi bilmiyorum. Kenarlar yerine üçgenler oluşturabilirseniz, normal hesaplama gerçekten kolay hale gelir.
EvilTak

Yanıtlar:


1

Her şeyden önce, ileri / geri / merkezi farklarla hesaplanırsa, normalleriniz tamamen iyi olmalıdır. Sorun şu ki, merkez noktanızı FeaturePoint işlevinizde yanlış yöne doğru hareket ettirmiş olursunuz, bu da minimumdan daha ileri gitmenize neden olur.

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;

Bunun nedeni, kodunuzun bir noktaya yakınsamayacağı ve bu nedenle voksel kutunuzdan atlayacağı için olur. Birisi ikili kontur açıklayabilir miyim kodu bilmiyorum ? noktanın uçağa yansıtıldığı bir projeksiyon yaklaşımı kullanması amaçlanmıştır:

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

ama aynı yöntem. Yansıtmayı orijinal kodunuza yeniden yazarsanız, bunun sonucu:

    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;

aşağıdakilere yeniden yazılabilir:

    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;

ve bu nedenle ilk kodla sonuçlanır. Üç düzlemsel olmayan düzlemde noktayı yansıtarak, yavaşça minimuma yaklaşır, çünkü resimde gösterildiği gibi her bir düzlemden noktanıza olan mesafeyi en aza indirirsiniz.

Kırmızı noktalar özellik noktasını, mavi çizgiler normalleri ve mor nokta düzlemde yansıtılan noktayı gösterir. 0.7 faktörünü de kullanmanız gerekmez, çünkü onsuz daha hızlı birleşmelidir. Bu yöntemi kullanırsanız, kesişmeyen düzlemleriniz varsa algoritmanın çalışmayabileceğini unutmayın.


Hey, 2 yıl sonra cevap almak için harika :) Hiç bir çözüm bulamadım, bu yüzden bu projeyi durdurdum, ancak bu bilgi ile tekrar ziyaret edeceğim ve nasıl gittiğini size bildireceğim. O zamana kadar +1.
ElDuderino

Müthiş! Sana yardım edebildiğim için memnunum. İşinize yarayıp yaramadığını bana bildirin.
Tim Rolff
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.