Mengapa hasil berbeda ketika mengubah urutan input di GL_LINES?

8

Kode:

#include <math.h>
#include <GL/glut.h>
#pragma comment(lib, "opengl32")
#include <gl/gl.h>
#include <gl/glu.h>

//Initialize OpenGL 
void init(void) {
    glClearColor(0, 0, 0, 0);

    glViewport(0, 0, 500, 500);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(0, 500, 0, 500, 1, -1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
} 

void drawLines(void) {
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0,1.0,1.0); 

    glBegin(GL_LINES);

    glVertex3d(0.5,         0.999,  0.0f);
    glVertex3d(499.501,     0.999,  0.0f);

    glEnd();

    glFlush();
} 


int _tmain(int argc, _TCHAR* argv[])
{
    glutInit(&argc, argv);  
    glutInitWindowPosition(10,10); 
    glutInitWindowSize(500,500); 
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); 

    glutCreateWindow("Example"); 
    init(); 
    glutDisplayFunc(drawLines); 
    glutMainLoop();

    return 0;
}

Deskripsi masalah:

  • Kode di atas akan menempatkan seluruh piksel baris bawah area klien jendela menjadi putih.
  • Jika saya menukar perintah perintah, dari glVertex3d(0.5, 0.999, 0.0f);glVertex3d(499.501, 0.999, 0.0f);ke glVertex3d(499.501, 0.999, 0.0f);glVertex3d(0.5, 0.999, 0.0f);, maka hanya piksel kiri bawah tidak akan menggambar.

Pemahaman saya:

  • Kedua simpul akhirnya akan ditransformasikan ke koordinat pusat piksel 2D yang (0,0, 0,499) dan (499,001, 0,499).
  • Algoritma menggambar garis hanya menerima titik integer pusat piksel sebagai input.
  • Jadi dua simpul akan menggunakan int (x + 0,5) dan akan menjadi (0, 0) dan (499, 0). Ini sesuai dengan hasil pertama tetapi bertentangan dengan hasil ketika urutan input berubah. Mengapa?
zombielei
sumber

Jawaban:

10

Perbedaan piksel yang dicakup tergantung pada urutan verteks terkait dengan aturan rasterisasi . Itu adalah aturan yang digunakan perangkat keras GPU untuk menentukan dengan tepat piksel mana yang dicakup oleh primitif.

Aturan rasterisasi agak halus. Salah satu tujuan mereka adalah untuk memastikan bahwa setiap kali Anda menggambar beberapa primitif yang terhubung dengan air, rasterisasi tidak pernah menghasilkan celah di antara mereka, juga tidak ada piksel yang tertutup dua kali (yang penting untuk pencampuran). Untuk mencapai itu, aturan memiliki beberapa kasus tepi dan sudut yang sangat spesifik. Secara harfiah ... mereka kasus yang berkaitan dengan tepi dan sudut primitif. :)

Dalam kasus garis, ini berfungsi sebagai berikut. Ada wilayah berbentuk berlian di sekitar setiap pusat piksel, seperti yang ditunjukkan dalam cuplikan diagram ini dari artikel MSDN yang ditautkan di atas.

aturan berlian untuk rasterisasi garis

Aturannya adalah pixel tertutup jika garis keluar berlian, ketika ditelusuri dari awal hingga akhir garis. Perhatikan bahwa piksel tidak tercakup jika garis masuk, tetapi tidak keluar, berlian. Ini memastikan bahwa jika Anda memiliki dua segmen garis yang terhubung ujung ke ujung, bahwa piksel pada titik akhir yang dibagikan hanya milik salah satu segmen dan karenanya tidak dirasterisasi dua kali.

Anda juga dapat melihat dalam diagram di atas bagaimana mengubah urutan simpul dapat memengaruhi piksel mana yang dicakup (yang tidak terjadi dengan segitiga, BTW). Diagram menunjukkan dua segmen garis yang identik kecuali untuk menukar urutan titik akhir, dan Anda dapat melihatnya membuat perbedaan piksel mana yang tercakup.

Segmen baris Anda agak analog dengan ini. Ujung kiri, pada (0,5, 0,999) berada di dalam berlian dari piksel (0, 0), sehingga tertutup ketika itu adalah simpul pertama (garis dimulai di dalam berlian, kemudian keluar) dan bukan ketika itu yang kedua vertex (garis memasuki berlian, dan berakhir di dalamnya, sehingga tidak pernah keluar). Sebenarnya, simpul-simpulnya dijepit ke titik tetap dengan 8 bit subpixel sebelum rasterisasi, jadi yang ini akhirnya dibulatkan menjadi (0,5, 1,0), yang persis di sudut atas berlian. Tergantung pada aturan rasterisasi, ini mungkin atau mungkin tidak dipertimbangkan di dalam berlian; sepertinya pada GPU Anda itu dianggap di dalam, tetapi ini dapat bervariasi di antara implementasi, karena spesifikasi GL tidak memakukan aturan sepenuhnya.

Nathan Reed
sumber