Kameranın arkasındaki bir noktayı doğru şekilde nasıl yansıtırım?


10

İlgi çekici noktaların üzerine bir ünlem işareti koyduğum bir 3D oyun yapıyorum.

2B ekranın neresinde yer almam gerektiğini öğrenmek için, 3B noktasını el ile işaretleyicinin olması gereken yere yansıtıyorum.

Şöyle görünüyor:

Harika görünümlü işaretleyiciler

Oldukça iyi görünüyor. İşaretçi ekranın dışında olduğunda, koordinatları ekrana sığacak şekilde kırparım. Şöyle görünüyor:

Daha da harika görünen işaretçiler

Şimdiye kadar fikir oldukça iyi gidiyor. Ancak, ilgi noktaları kameranın arkasında olduğunda, ortaya çıkan X, Y koordinatları ters çevrilir (pozitif / negatif olarak) ve işaretçiyi ekranın karşı köşesinde görünmesini sağlarım, şöyle:

İşaretler o kadar harika değil

(Öngörülen-sonra-kenetli nokta işaretleyicinin ucu. İşaretçinin dönüşüne aldırmayın)

Frustumun arkasındaki koordinatlar X ve Y'de ters çevrildiğinden mantıklı geliyor. Yani yaptığım şey koordinatları kameranın arkasındayken tersine çevirmek. Ancak, yine de koordinatların ters çevrilmesi gereken durumun tam olarak ne olduğunu bilmiyorum.

Şu anda benim projeksiyon kodum gibi görünüyor (Sharp # ile C #):

    public override PointF ProjectPosition(float viewportWidth, float viewportHeight, float y)
    {
        var projectionMatrix = Matrix.PerspectiveFovRH(GetCalibratedFOV(Camera.FOV, viewportWidth, viewportHeight), viewportWidth / viewportHeight, Camera.Near, Camera.Far);
        var viewMatrix = Matrix.LookAtRH(new Vector3(Camera.PositionX, Camera.PositionY, Camera.PositionZ), new Vector3(Camera.LookAtX, Camera.LookAtY, Camera.LookAtZ), Vector3.UnitY);
        var worldMatrix = Matrix.RotationY(Rotation) * Matrix.Scaling(Scaling) * Matrix.Translation(PositionX, PositionY, PositionZ);
        var worldViewProjectionMatrix = worldMatrix * viewMatrix * projectionMatrix;

        Vector4 targetVector = new Vector4(0, y, 0, 1);
        Vector4 projectedVector = Vector4.Transform(targetVector, worldViewProjectionMatrix);

        float screenX = (((projectedVector.X / projectedVector.W) + 1.0f) / 2.0f) * viewportWidth;
        float screenY = ((1.0f - (projectedVector.Y / projectedVector.W)) / 2.0f) * viewportHeight;
        float screenZ = projectedVector.Z / projectedVector.W;

        // Invert X and Y when behind the camera
        if (projectedVector.Z < 0 ||
            projectedVector.W < 0)
        {
            screenX = -screenX;
            screenY = -screenY;
        }

        return new PointF(screenX, screenY);
    }

Gördüğünüz gibi, şu anki fikrim Z veya W koordinatları negatif olduğunda koordinatları tersine çevirmek. Çoğu zaman çalışır, ancak hala çalışmadığı bazı çok spesifik kamera konumları vardır. Özellikle bu nokta bir koordinatın çalıştığını ve diğerinin çalışmadığını gösterir (doğru konum sağ alt olmalıdır):

İşaretçiler yarı harika

Ne zaman ters çevirme denedim:

  • Z negatif (bu benim için en anlamlı olan şey)
  • W negatif (negatif W değerinin anlamını anlamıyorum)
  • Ya Zya Wnegatif (şu anda çoğu zaman ne çalışıyor)
  • Zve Wfarklı işaretlere sahipler, aka: Z / W < 0(bana mantıklı geliyor. yine de çalışmıyor)

Ancak yine de tüm noktalarımın doğru bir şekilde yansıtıldığı tutarlı bir yol bulamadık.

Herhangi bir fikir?

Yanıtlar:


2

Biraz farklı yapmaya ne dersiniz?

Her zamanki gibi başlayın ve tüm işaretleyicileri görünüm alanı içinde oluşturun. Bir işaretleyici görünümün dışındaysa - devam edin:

  1. İşaretçinin konumlarını alma A
  2. Kameranın konumunu alın B(belki biraz önünde)
  3. ABBu ikisi arasında 3B vektör hesapla
  4. Bu vektörü 2D ekran sınırlarına dönüştürün ve normalleştirin ( Agörünüm merkezinde Bolacak ve görünüm sınırına ulaşacak)

resim açıklamasını buraya girin

Renkli çizgiler ABvektörlerdir. İnce siyah çizgiler karakter yüksekliğidir. Sağ tarafta 2D ekran var

  1. Belki kameranın arkasındaki her şeyin alt kenara kadar devam ettiği konusunda bir kural ekleyin
  2. İşaretçiyi, işaretleyicilerin boyutları ve çakışmalara göre ekran alanına çizin

Görünüm penceresinden çıkan işaretçinin konumlar arasında atlayıp atlamayacağını görmek için bunu denemeniz gerekebilir. Bu durumda, işaretleyici görünüm penceresinin sınırına yaklaştığında konumu düzeltmeyi deneyebilirsiniz.


Bu adım 4 tam olarak nasıl çalışır?
Panda Pijama

@PandaPajama: Cevaba biraz ekledim. Şimdi daha açık mı?
Kromster

Pek sayılmaz. Vektörü nasıl "dönüştürüyorsunuz ve normalleştiriyorsunuz"? Eğer bu bir projeksiyon matrisi uygulayarak, o zaman yaptığım şeyi yapıyorsunuz
Panda Pyjama

@PandaPajama: Anladığım kadarıyla kamera alanında yaptığınızı (dolayısıyla negatif ZW sorunu). Bunu dünya uzayında yapmayı ve ancak daha sonra ekran uzayına dönüştürmeyi öneririm.
Kromster

Ancak, hesaplayarak AB, hedef konumu dünya koordinatlarından kamera koordinatlarına dönüştürmüyor muyum?
Panda Pijama

1

Üç vektörler Verilen [camera position], [camera direction]ve [object position]. Dot ürünü hesaplayın [camera direction]ve [object position]-[camera position]. Sonuç negatifse, nesne kameranın arkasındadır.

Kod doğru anladım göz önüne alındığında bu olurdu:

if((PositionX - Camera.PositionX) * Camera.LookAtX
    + (PositionY - Camera.PositionY) * Camera.LookAtY
    + (PositionZ - Camera.PositionZ) * Camera.LookAtZ
    < 0)

Yolduğunu PositionY + (y * Scaling). Bu gece bir deneyeceğim
Panda Pyjama

Bununla gerçekten kameranın arkasında ne zaman olduğunu biliyorum. Ancak bu, izdüşümün Z = 0'da tekilliğe ulaşmasını engellemez.
Panda Pijama

@PandaPajama Bu pratik bir sorun mu? ZTam olarak olma olasılığı 0çok küçük olmalı, çoğu oyuncu bunu asla deneyimlemez ve beklenen birkaç durumda oyun etkisi ihmal edilebilir tek kare görsel bir aksaklıktır. Zamanınızın başka bir yerde daha iyi geçirildiğini söyleyebilirim.
aaaaaaaaaaaa

0

Sadece test edin (yansıtılan W <0), değil (Z <0 || W <0).

Bazı klip alanı formülasyonlarında ( öksürük OpenGL öksürüğü ), görünür aralık pozitif ve negatif Z değerlerini içerirken, W her zaman kameranın önünde pozitif ve arkasında negatiftir.


Ben sadece W <0 ile denedim, ama hala yanlış öngörülen yerler var.
Panda Pijama

0
float screenX = (((projectedVector.X / abs(projectedVector.W)) + 1.0f) / 2.0f) * viewportWidth;
float screenY = ((1.0f - (projectedVector.Y / abs(projectedVector.W))) / 2.0f) * viewportHeight;

Ve bu parçayı kaldır

// Invert X and Y when behind the camera
if (projectedVector.Z < 0 || projectedVector.W < 0)
{
    screenX = -screenX;
    screenY = -screenY;
}

-2

Fotoğrafı otomatik olarak fotoğraf makinesine bakacak ve resmi otomatik olarak doğru yere ekrana çekecek bir pano deneyin.


3
Soruyu okudun mu?
Kromster

Üzgünüm, açıklamama izin verin - Bir reklam panosunun çıktısını almak, ölçeklendirmeden kurtulmak mümkündür (bu yüzden 2D'dir, ancak 3d konumunu takip eder) ve daha sonra bazı matris matematiği kullanarak ekranın sınırlarında tutmak mümkündür.
Michael Langford

Soru özellikle " matris matematiği kullanarak ekranın sınırlarında
kalsın
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.