Bagaimana cara memproyeksikan titik di belakang kamera dengan benar?

10

Saya membuat game 3D di mana saya menempatkan tanda seru di atas tempat menarik.

Untuk mengetahui di mana di layar 2D saya harus meletakkan penanda saya, saya secara manual memproyeksikan titik 3D di mana penanda itu berada.

Ini terlihat seperti ini:

Spidol tampak luar biasa

Terlihat bagus. Ketika penanda berada di luar layar, saya cukup klip koordinat sehingga mereka masuk ke layar. Ini terlihat seperti ini:

Spidol tampak lebih dahsyat

Sejauh ini idenya berjalan cukup baik. Namun, ketika tempat menarik berada di belakang kamera, koordinat X, Y yang dihasilkan dibalik (seperti dalam positif / negatif), dan saya mendapatkan penanda untuk muncul di sudut berlawanan dari layar, seperti ini:

Spidol tidak begitu luar biasa

(Titik yang diproyeksikan-kemudian-dijepit adalah ujung marker. Jangan pedulikan rotasi marker)

Masuk akal, karena koordinat di belakang frustum terbalik dalam X dan Y. Jadi yang saya lakukan adalah membalikkan koordinat ketika mereka berada di belakang kamera. Namun, saya masih tidak tahu persis kondisi kapan koordinat harus dibalik.

Saat ini seperti apa kode proyeksi saya (dalam C # dengan SharpDX):

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

Seperti yang Anda lihat, ide saya saat ini adalah membalikkan koordinat ketika koordinat Z atau W negatif. Ini berfungsi - hampir - sebagian besar waktu, tetapi masih ada beberapa lokasi kamera yang sangat spesifik di mana ia tidak bekerja. Secara khusus, titik ini menunjukkan satu koordinat berfungsi dan yang lainnya tidak (lokasi yang benar adalah kanan bawah):

Penanda setengah dahsyat

Saya telah mencoba membalikkan ketika:

  • Z negatif (ini yang paling masuk akal bagi saya)
  • W negatif (saya tidak mengerti arti dari nilai W negatif)
  • Baik Zatau Wnegatif (apa yang saat ini paling sering bekerja)
  • Zdan Wdari tanda yang berbeda, alias: Z / W < 0(masuk akal bagi saya. tidak bekerja sekalipun)

Tetapi masih belum menemukan cara yang konsisten dengan semua poin saya diproyeksikan dengan benar.

Ada ide?

Piyama Panda
sumber

Jawaban:

2

Bagaimana kalau melakukannya sedikit berbeda?

Mulai seperti biasa dan render semua marker dalam viewport. Jika marker berada di luar viewport - lanjutkan:

  1. Dapatkan posisi marker A
  2. Dapatkan posisi kamera B(mungkin sedikit di depannya)
  3. Hitung vektor 3D di ABantara keduanya
  4. Konversikan dan normalkan vektor itu menjadi batas layar 2D ( Aakan berada di pusat tampilan dan Bakan mengenai batas tampilan)

masukkan deskripsi gambar di sini

Garis warna adalah ABvektor. Garis hitam tipis adalah ketinggian karakter. Di sebelah kanan adalah layar 2D

  1. Mungkin menambahkan aturan, bahwa segala sesuatu di belakang kamera selalu berjalan ke tepi bawah
  2. Gambar penanda di ruang layar sehubungan dengan ukuran spidol dan tumpang tindih

Anda mungkin perlu mencobanya untuk melihat apakah marker mematikan viewport akan melompat di antara posisi. Jika itu terjadi, Anda dapat mencoba menempatkan posisi ketika marker mendekati perbatasan viewport.

Kromster
sumber
Bagaimana tepatnya langkah 4 itu berfungsi?
Piyama Panda
@ Pandaanda: Saya telah menambahkan sedikit jawaban. Apakah sekarang lebih jelas?
Kromster
Tidak juga. Bagaimana Anda "mengubah dan menormalkan" vektor? Jika itu dengan menerapkan matriks proyeksi, maka Anda melakukan hal yang sama yang saya lakukan
Panda Pajama
@ Pamanda: Sejauh yang saya mengerti Anda melakukannya di ruang kamera (maka masalah ZW negatif). Saya menyarankan untuk melakukannya di ruang dunia dan hanya kemudian dikonversi ke layar-ruang.
Kromster
Tetapi, dengan menghitung AB, apakah saya tidak mengubah posisi target dari koordinat dunia ke koordinat kamera?
Piyama Panda
1

Diberikan tiga vektor [camera position], [camera direction]dan [object position]. Hitung produk titik dari [camera direction]dan [object position]-[camera position]. Jika hasilnya negatif, objek berada di belakang kamera.

Mengingat saya memahami kode Anda dengan benar, itu akan menjadi:

if((PositionX - Camera.PositionX) * Camera.LookAtX
    + (PositionY - Camera.PositionY) * Camera.LookAtY
    + (PositionZ - Camera.PositionZ) * Camera.LookAtZ
    < 0)
aaaaaaaaaaaa
sumber
Yadalah PositionY + (y * Scaling). Saya akan mencobanya malam ini
Piyama Panda
Dengan ini saya memang bisa tahu kapan titik di belakang kamera. Namun, ini tidak mencegah proyeksi untuk mencapai singularitas pada Z = 0.
Panda Pajama
@PandaPajama Apakah itu masalah praktis? Kemungkinan Zmenjadi persis 0harus sangat kecil, sebagian besar pemain tidak akan pernah mengalaminya, dan dampak gameplay dalam beberapa kasus yang diharapkan adalah kesalahan visual bingkai tunggal diabaikan. Saya akan mengatakan waktu Anda lebih baik dihabiskan di tempat lain.
aaaaaaaaaaaa
0

Tes saja (diproyeksikan.W <0), bukan (Z <0 || W <0).

Dalam beberapa formulasi ruang klip ( batuk OpenGL batuk ), kisaran terlihat termasuk nilai Z positif dan negatif, sedangkan W selalu positif di depan kamera dan di belakang negatif.

jaybird
sumber
Saya sudah mencoba dengan hanya W <0, tetapi masih ada beberapa tempat yang salah proyeksi.
Panda Pajama
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;

Dan hapus bagian ini

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

Coba papan reklame, yang secara otomatis akan membuat gambar menghadap kamera, dan secara otomatis akan menggambar gambar di layar di lokasi yang tepat

Michael Langford
sumber
3
Sudahkah Anda membaca pertanyaan?
Kromster
Maaf, izinkan saya menjelaskan - Dimungkinkan untuk mengambil output dari papan iklan, menyingkirkan penskalaan, (jadi 2D tetapi mengikuti posisi 3d), lalu menyimpannya di batas layar menggunakan beberapa matematika matriks.
Michael Langford
Pertanyaannya secara khusus tentang "dan kemudian menyimpannya dalam batas-batas layar menggunakan beberapa matriks matematika "
Kromster