OpenGL: glNormal ile neden normal bir ayar yapmam gerekiyor?


19

OpenGL'nin bazı temellerini öğreniyorum ama neden glNormalköşeleri normalleştirmek için bir çağrı olduğunu merak ediyorum .

Bunun gibi basit bir üçgen oluşturursam:

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

Normaller, geometrik ilkel türüyle örtük olarak tanımlanmamalı mı? Normal bir ayarlamazsam OpenGL hesaplar mı?


7
Bu kodun kullanımdan kaldırılmış sabit işlevli boru hattı kullandığını ve modern OpenGL'de hem normal hem de köşe noktalarının yalnızca genel köşe özellikleri olduğunu belirtmek isterim.
Bartek Banachewicz

4
Anında mod kullanmayın. Kullanımdan kaldırıldı. Modern 3D Grafik Programlamayı Öğrenmenizi önemle tavsiye ederim .
sağda

3
Acil mod kullanımı hakkındaki yorumun burada tamamen alakasız olduğunu düşünüyorum. Evet, bunu kullanmamalıyız, ancak sorunun noktası acil mod, tepe dizileri, VBO, genel attribs, sabit attribs veya tost parçalarına bakılmaksızın aynı kalır.
Maximus Minimus

1
Otomatik olarak belirlenmemesinin nedeni, aydınlatmayı çok poligon haline getirmesidir (daha iyi bir kelime olmaması nedeniyle). Bununla demek istediğim, bir üçgen üzerindeki tüm verticiler aynı normal değere sahip olacaktı. Düz yüzeyler için beklediğimiz budur, ancak bir kişinin yüzünün bir modeli olsaydı, yüzdeki her üçgen aynı aydınlatma değerine sahip olurdu (çokgenleri görmeyi çok kolay hale getirir). Kendi normal değerlerinizi belirterek, işlerin daha pürüzsüz görünmesini sağlayabilirsiniz (normalde, geçerli tepe noktasını içeren tüm üçgenlerin normal değerlerini ortalayarak normal bir değer bulursunuz).
Benjamin Danger Johnson

Yanıtlar:


30

glNormalAralarında birden çok kez arama yapmak glBegin/End, ilkel yerine köşe başına normal bir değer belirlemenize olanak tanır.

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

Birden çok üçgenden oluşan daha karmaşık bir ağ için, bir tepe noktasındaki normalin, sadece ait olduğu üçgenin normaline değil, çevredeki geometrinin diğer üçgenlerine bağlı olmasını istersiniz.

İşte aynı geometriye bir örnek:

  • sol köşe başına normallerde - bu yüzden bir yüzün her tepe noktasının farklı bir normali vardır (bu küre için tüm normaller merkezden dışarıya doğru işaret eder) ve
  • sağda başına yüze normaller - Her köşe (yalnızca bir kez belirtin veya yana varsayılan Normal değeri kullanır çünkü aynı Normal değeri alır (0,0,1)):

solda köşe başı normaller, sağda yüz başına normaller

Varsayılan gölge modeli ( GL_SMOOTH), yüz köşelerinin normallerinin yüz boyunca enterpolasyonuna neden olur. For başına tepe normaller gölgeli pürüzsüz alanda bu sonuçlar, ancak için başına yüze normaller Eğer karakteristik yönlü bir bakışla sonunda.


4

Hayır, GL sizin için bir norm hesaplamaz. Normalin ne olması gerektiğini bilemez. İlkelleriniz, rasterleştirme (ve birkaç yer) dışında bir şey ifade etmiyor. GL, hangi yüzlerin (üçgenlerin) bir tepe noktasını paylaştığını bilmez (farklı veri yapıları kullanmadan çalışma zamanında bunu hesaplamak çok pahalıdır).

Ortak köşeleri bulmak ve normalleri hesaplamak için bir kafesin "üçgen çorbası" nı önceden işlemeniz gerekir. Bu, oyun hız için çalışmadan önce yapılmalıdır.

Köşede geometrik olarak paylaşılan bir tepe noktasının olduğu sert kenarlar gibi durumlar da vardır, ancak ayrı normaller istediğiniz için doğru şekilde yanar. GL / D3D görüntü oluşturma modelinde, bunun için her biri ayrı bir normal olan iki ayrı köşeye (aynı konumda) ihtiyacınız vardır.

Tüm bunlara UV'ler ve doku eşlemesi için ihtiyacınız olacak.


3

Kısa cevap aslında kalmamasıdır var hiç normal ayarlamak için. Normal vektörler yalnızca OpenGL ışıklandırması (veya belki de bunlara bağlı olabilecek başka bir hesaplama) yapıyorsanız, ancak bu sizin için geçerli değilse (veya ışıklandırma yapmak için başka bir yöntem kullanıyorsanız, örneğin ışık haritaları) kullanılır. ) o zaman tüm glNormal çağrılarını mutlu bir şekilde atlayabilirsiniz.

Öyleyse, glNormal belirtmenin mantıklı olduğu ve ona biraz daha baktığı bir şey yaptığınızı varsayalım.

glNormal, ilkel veya tepe noktası başına belirtilebilir. İlkel normaller ile bunun anlamı, ilkel yüzdeki her tepe için tüm normallerin aynı yönde olmasıdır. Bazen istediğiniz budur, ancak bazen değildir - köşe başına normallerin işe yaradığı yer, her bir tepe noktasının kendi yüzüne sahip olmasına izin vermesidir.

Klasik bir küre örneğini ele alalım. Bir küredeki her bir üçgen aynı normal olsaydı, sonuç oldukça yönlü olurdu. Muhtemelen istediğin bu değil, bunun yerine pürüzsüz bir gölgeleme istiyorsun. Yani yaptığınız şey, ortak bir tepe noktasını paylaşan ve pürüzsüz gölgeli bir sonuç elde eden her üçgen için normalleri ortalamaktır.


2

glNormalDağınık yıldırım ile nasıl çalıştığının bazı ayrıntılarını gösteren minimal örnek .

displayFonksiyonun yorumları her üçgenin ne anlama geldiğini açıklar.

resim açıklamasını buraya girin

#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

OpenGL'de her tepe noktasının kendi ilişkili normal vektörü vardır.

Normal vektör, tepe noktasının ne kadar parlak olduğunu belirler; bu daha sonra üçgenin ne kadar parlak olduğunu belirlemek için kullanılır.

Bir yüzey ışığa dik olduğunda, paralel bir yüzeyden daha parlaktır.

glNormal izleyen tüm köşe noktaları için kullanılan geçerli normal vektörü ayarlar.

Tüm önce normal için başlangıç değeri glNormalolan 0,0,1.

Normal vektörlerde norm 1 olmalıdır , aksi takdirde renkler değişir! glScaleayrıca normalin uzunluğunu değiştirir! glEnable(GL_NORMALIZE);OpenGL'nin normlarını bizim için otomatik olarak 1'e ayarlamasını sağlar. Bu GIF bunu güzel bir şekilde gösteriyor.

Kaynakça:


Güzel cevap, ama neden eski bir soruya ve eski bir teknolojiye odaklanıyoruz?
Vaillancourt

@AlexandreVaillancourt teşekkürler! Eski soru: neden olmasın :-) Eski teknoloji: bugün daha iyi bir yol nedir?
Ciro Santilli 事件 改造 中心 法轮功 六四 事件

Modern OpenGL glNormal belirtmek yerine arabellek nesneleri kullandığını düşünüyorum.
Vaillancourt

1
Gerçekten, glNormal ve glVertex ve benzerleri modern OpenGL'ye girmişler ve bundan bir süre önce kaldırılmışlardı ... aslında bu soru ilk sorulduğunda bile kullanımdan kaldırılmışlardı.

0

Kodlayıcıya bağlı bazı özellikleri uygulamak için glNormal'a ihtiyacınız vardır. Örneğin, sert kenarları korumak için normaller oluşturmak isteyebilirsiniz.


4
Belki cevabınızı biraz geliştirmek istersiniz.
Bartek Banachewicz

1
Cevabımı geliştirmemi istiyorsanız zayıf noktaları belirtmeniz gerekir. Yine de üzerinde durulabilirim. Xblax'ın düşündüğü gibi, her normal "aynı" şekilde hesaplanmalıdır. Her bir piksel için her yüz için enterpolasyonlu normaller arasındaki farka rağmen. Biraz daha konuya geçelim.
AB.

1
Bazen sert kenarı korumak ve daha belirgin hale getirmek isteyebilirsiniz. İşte bazı örnek görüntüler: titan.cs.ukzn.ac.za/opengl/opengl-d5/trant.sgi.com/opengl/… PS. Çifte paylaşım için özür dilerim. SE arayüzünde yeniyim
AB.

bir "düzenle" düğmesi var.
Bartek Banachewicz

1
5 dakika sonra düzenleme yapamazsınız
AB.
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.