OpenGL: mengapa saya harus menetapkan yang normal dengan glNormal?

19

Saya belajar beberapa dasar-dasar OpenGL tapi saya bertanya-tanya mengapa ada panggilan glNormaluntuk mengatur simpul normal.

Jika saya membuat segitiga sederhana seperti ini:

glBegin(GL_TRIANGLES);
    glVertex3f(0,0,0);
    glVertex3f(1,0,0);
    glVertex3f(0,1,0);
glEnd();

Haruskah normals didefinisikan secara implisit oleh tipe geometri primitif? Jika saya tidak menetapkan normal apakah OpenGL akan menghitungnya?

xblax
sumber
7
Saya hanya ingin menunjukkan bahwa kode ini menggunakan pipeline fungsi tetap yang sudah tidak digunakan lagi, dan dalam OpenGL modern baik normals dan verteks hanyalah atribut vertex generik.
Bartek Banachewicz
4
Jangan gunakan mode langsung. Sudah usang. Saya sangat merekomendasikan Belajar Pemrograman Grafik 3D Modern .
rightfold
3
Saya pikir komentar tentang penggunaan mode langsung sama sekali tidak relevan di sini. Ya, kita tidak boleh menggunakannya, tetapi inti pertanyaannya tetap sama terlepas dari apakah itu mode langsung, array vertex, VBO, attrib generik, attrib tetap, atau potongan roti panggang.
Maximus Minimus
1
Alasannya tidak ditentukan secara otomatis adalah karena itu akan membuat pencahayaan sangat poligoni (karena tidak ada kata yang lebih baik). Yang saya maksud dengan ini adalah, semua simpul pada segitiga akan memiliki nilai normal yang sama. Untuk permukaan datar inilah yang kami harapkan, namun jika Anda memiliki model wajah orang, setiap segitiga pada wajah akan memiliki nilai pencahayaan yang sama (membuatnya sangat mudah untuk melihat poligon). Dengan menentukan nilai normal Anda sendiri, Anda dapat membuat segalanya tampak lebih halus (biasanya Anda menemukan nilai normal dengan rata-rata semua segitiga yang berisi titik saat ini).
Benjamin Danger Johnson

Jawaban:

30

Memanggil glNormalbeberapa kali antara glBegin/Endmemungkinkan Anda untuk mengatur normal per simpul bukan primitif.

glBegin(GL_TRIANGLES);
    glNormal3f(0,0,1);
    glVertex3f(0,0,0);
    glNormal3f(0,0,1);
    glVertex3f(1,0,0);
    glNormal3f(0,0,1);
    glVertex3f(0,1,0);
glEnd();

Untuk mesh yang lebih kompleks yang terdiri dari beberapa segitiga Anda ingin normal pada satu titik bergantung pada segitiga lain dari geometri sekitarnya dan bukan hanya normal dari segitiga yang dimilikinya.

Berikut adalah contoh geometri yang sama dengan:

  • di sebelah kiri per-simpul normalnya - sehingga setiap simpul wajah memiliki normal yang berbeda (untuk bidang ini berarti semua normals mengarah keluar dari pusatnya), dan
  • di sebelah kanan per-wajah normals - setiap titik mendapatkan nilai normal yang sama (karena Anda menetapkannya hanya sekali, atau karena menggunakan nilai normal default (0,0,1)):

per-vertex normals on the left, per-face normals on the right

Model naungan default ( GL_SMOOTH) menyebabkan normals dari simpul wajah diinterpolasi melintasi wajah. Untuk per-vertex normals ini menghasilkan bulatan berbayang halus, tetapi untuk per-face normals Anda berakhir dengan tampilan segi karakteristik.

Eric
sumber
4

Tidak, GL tidak menghitung norma untuk Anda. Tidak tahu apa yang seharusnya. Primitif Anda tidak berarti apa pun di luar rasterisasi (dan beberapa tempat lain). GL tidak tahu wajah mana (segitiga) yang berbagi titik (menghitung ini pada saat runtime sangat mahal tanpa menggunakan struktur data yang berbeda).

Anda perlu memproses "sup segitiga" mesh untuk menemukan simpul umum dan menghitung normalnya. Ini harus dilakukan sebelum permainan berjalan untuk kecepatan.

Ada juga kasus-kasus seperti tepi keras di mana secara geometris ada simpul bersama di sudut, tetapi Anda ingin normalnya terpisah sehingga menyala dengan benar. Dalam model rendering GL / D3D, Anda memerlukan dua simpul terpisah (pada posisi yang sama) untuk ini, masing-masing dengan normal yang terpisah.

Anda juga memerlukan semua ini untuk pemetaan tekstur dan UV.

Sean Middleditch
sumber
3

Jawaban singkatnya adalah Anda tidak harus menetapkan normal sama sekali. Vektor normal hanya digunakan jika Anda melakukan pencahayaan OpenGL (atau mungkin beberapa perhitungan lain yang mungkin bergantung pada mereka), tetapi jika itu tidak berlaku untuk Anda (atau jika Anda menggunakan beberapa metode lain untuk melakukan pencahayaan, mis. Peta cahaya ) maka Anda dapat dengan senang hati menghilangkan semua panggilan glNormal.

Jadi mari kita asumsikan bahwa Anda melakukan sesuatu di mana menentukan glNormal masuk akal dan melihatnya lagi.

glNormal dapat ditentukan per primitif atau per-verteks. Dengan normals per-primitif, semua artinya adalah semua normals untuk setiap simpul di wajah primitif dalam arah yang sama. Kadang-kadang ini yang Anda inginkan tetapi terkadang tidak - di mana normalnya per-simpul berguna adalah bahwa mereka memungkinkan setiap simpul memiliki wajah sendiri.

Ambil contoh klasik bola. Jika setiap segitiga dalam bola memiliki normal yang sama maka hasil akhirnya akan cukup faceted. Mungkin itu bukan yang Anda inginkan, Anda justru menginginkan shading yang halus. Jadi yang Anda lakukan adalah rata-rata normals untuk setiap segitiga yang berbagi titik umum, dan mendapatkan hasil yang diarsir halus.

Maximus Minimus
sumber
2

Contoh minimal yang menggambarkan beberapa detail tentang bagaimana glNormalbekerja dengan petir yang menyebar.

Komentar displayfungsi menjelaskan arti setiap segitiga.

masukkan deskripsi gambar di sini

#include <stdlib.h>

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

/* Triangle on the x-y plane. */
static void draw_triangle() {
    glBegin(GL_TRIANGLES);
    glVertex3f( 0.0f,  1.0f, 0.0f);
    glVertex3f(-1.0f, -1.0f, 0.0f);
    glVertex3f( 1.0f, -1.0f, 0.0f);
    glEnd();
}

/* A triangle tilted 45 degrees manually. */
static void draw_triangle_45() {
    glBegin(GL_TRIANGLES);
    glVertex3f( 0.0f,  1.0f, -1.0f);
    glVertex3f(-1.0f, -1.0f,  0.0f);
    glVertex3f( 1.0f, -1.0f,  0.0f);
    glEnd();
}

static void display(void) {
    glColor3f(1.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glPushMatrix();

    /*
    Triangle perpendicular to the light.
    0,0,1 also happens to be the default normal if we hadn't specified one.
    */
    glNormal3f(0.0f, 0.0f, 1.0f);
    draw_triangle();

    /*
    This triangle is as bright as the previous one.
    This is not photorealistic, where it should be less bright.
    */
    glTranslatef(2.0f, 0.0f, 0.0f);
    draw_triangle_45();

    /*
    Same as previous triangle, but with the normal set
    to the photorealistic value of 45, making it less bright.

    Note that the norm of this normal vector is not 1,
    but we are fine since we are using `glEnable(GL_NORMALIZE)`.
    */
    glTranslatef(2.0f, 0.0f, 0.0f);
    glNormal3f(0.0f, 1.0f, 1.0f);
    draw_triangle_45();

    /*
    This triangle is rotated 45 degrees with a glRotate.
    It should be as bright as the previous one,
    even though we set the normal to 0,0,1.
    So glRotate also affects the normal!
    */
    glTranslatef(2.0f, 0.0f, 0.0f);
    glNormal3f(0.0, 0.0, 1.0);
    glRotatef(45.0, -1.0, 0.0, 0.0);
    draw_triangle();

    glPopMatrix();
    glFlush();
}

static void init(void) {
    GLfloat light0_diffuse[] = {1.0, 1.0, 1.0, 1.0};
    /* Plane wave coming from +z infinity. */
    GLfloat light0_position[] = {0.0, 0.0, 1.0, 0.0};
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);
    glLightfv(GL_LIGHT0, GL_POSITION, light0_position);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light0_diffuse);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glColorMaterial(GL_FRONT, GL_DIFFUSE);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_NORMALIZE);
}

static void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0, 7.0, -1.0, 1.0, -1.5, 1.5);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(800, 200);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return EXIT_SUCCESS;
}

Teori

Dalam OpenGL setiap vertex memiliki vektor normal yang terkait.

Vektor normal menentukan seberapa terang verteks itu, yang kemudian digunakan untuk menentukan seberapa terang segitiga itu.

Saat permukaan tegak lurus terhadap cahaya, permukaan lebih terang dari permukaan paralel.

glNormal mengatur vektor normal saat ini, yang digunakan untuk semua simpul berikut.

Nilai awal untuk normal sebelum kita semua glNormaladalah 0,0,1.

Vektor normal harus memiliki norma 1, atau warna berubah! glScalejuga mengubah panjang normals! glEnable(GL_NORMALIZE);membuat OpenGL secara otomatis mengatur norma mereka ke 1 untuk kita. GIF ini menggambarkan hal itu dengan indah.

Bibliografi:

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
sumber
Jawaban yang bagus, tetapi mengapa berfokus pada pertanyaan lama dan teknologi lama?
Vaillancourt
@AlexandreVaillancourt terima kasih! Pertanyaan lama: mengapa tidak :-) Teknologi lama: apa cara yang lebih baik hari ini?
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
Saya pikir OpenGL modern menggunakan objek buffer alih-alih menentukan glNormal.
Vaillancourt
1
Memang, glNormal dan glVertex dan sejenisnya telah hilang di OpenGL modern, dan sudah ditinggalkan beberapa waktu sebelumnya ... mereka sebenarnya sudah ditinggalkan bahkan ketika pertanyaan ini awalnya diajukan.
0

Anda memerlukan glNormal untuk mengimplementasikan beberapa fitur yang tergantung pada coder. Sebagai contoh, Anda mungkin ingin membuat data normal mempertahankan tepi yang sulit.

AB.
sumber
4
Mungkin Anda ingin sedikit meningkatkan jawaban Anda.
Bartek Banachewicz
1
Jika Anda ingin saya meningkatkan jawaban saya, Anda perlu menunjukkan titik-titik lemah. Saya bisa menguraikannya. Seperti yang digunakan xblax untuk berpikir, setiap normal "harus" dihitung dengan cara yang sama. Meskipun ada perbedaan antara normals yang diinterpolasi untuk setiap pixel vs setiap wajah. Mari kita bahas lebih dalam tentang topik ini.
AB.
1
Terkadang Anda mungkin ingin mempertahankan keunggulan dan membuatnya lebih khas. Berikut adalah beberapa contoh gambar: titan.cs.ukzn.ac.za/opengl/opengl-d5/trant.sgi.com/opengl/… PS. Maaf untuk posting ganda. Saya baru mengenal antarmuka SE
AB.
ada tombol "edit".
Bartek Banachewicz
1
Anda tidak dapat mengedit setelah 5 menit
AB.