OpenGL: ทำไมฉันต้องตั้งค่าปกติด้วย glNormal


19

ฉันกำลังเรียนรู้พื้นฐานของ OpenGL แต่ฉันสงสัยว่าทำไมมีการเรียกglNormalให้ตั้งค่าจุดยอดนิยมตามปกติ

ถ้าฉันสร้างสามเหลี่ยมอย่างง่ายเช่นนี้:

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

ไม่ควรกำหนดบรรทัดฐานโดยปริยายโดยชนิดของเรขาคณิตดั้งเดิม? หากฉันไม่ได้ตั้งค่าปกติ OpenGL จะคำนวณหรือไม่


7
ฉันต้องการจะชี้ให้เห็นว่ารหัสนี้ใช้ไปป์ไลน์ฟังก์ชั่นคงที่ที่เลิกใช้แล้วและใน OpenGL สมัยใหม่ทั้งบรรทัดฐานและจุดยอดเป็นเพียงคุณลักษณะจุดสุดยอดทั่วไป
Bartek Banachewicz

4
อย่าใช้โหมดทันที มันเลิกใช้แล้ว ผมขอแนะนำโปรแกรมการเรียนรู้ที่ทันสมัยกราฟิก 3D
rightfold

3
ฉันคิดว่าความคิดเห็นเกี่ยวกับการใช้โหมดเร่งด่วนไม่เกี่ยวข้องอย่างสมบูรณ์ที่นี่ ใช่หนึ่งไม่ควรใช้ แต่ประเด็นของคำถามยังคงเหมือนเดิมโดยไม่คำนึงถึงว่าเป็นโหมดในทันทีอาร์เรย์ยอด, VBO, attribs ทั่วไป, attribs คงที่หรือบิตของขนมปัง
Maximus Minimus

1
เหตุผลที่มันไม่ได้ถูกกำหนดโดยอัตโนมัติก็เพราะว่ามันจะทำให้แสงหลายเหลี่ยมมาก (สำหรับการขาดคำที่ดีกว่า) สิ่งที่ผมหมายถึงคือทุกจุดในสามเหลี่ยมจะมีค่าปกติเหมือนกัน สำหรับพื้นผิวเรียบนี่คือสิ่งที่เราคาดหวังอย่างไรก็ตามถ้าคุณมีแบบจำลองของบุคคลใบหน้าแต่ละรูปสามเหลี่ยมบนใบหน้าจะมีค่าแสงเท่ากัน (ทำให้ง่ายที่จะเห็นรูปหลายเหลี่ยม) ด้วยการระบุค่าปกติของคุณเองคุณสามารถทำให้สิ่งต่าง ๆ ดูราบรื่นขึ้น (โดยปกติคุณจะพบค่าปกติโดยเฉลี่ยค่าเฉลี่ยของสามเหลี่ยมทั้งหมดที่มีจุดยอดปัจจุบัน)
Benjamin Danger Johnson

คำตอบ:


30

การโทรglNormalหลายครั้งระหว่างglBegin/Endช่วยให้คุณตั้งค่าปกติต่อจุดยอดแทนที่จะเป็นแบบดั้งเดิม

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

สำหรับตาข่ายที่ซับซ้อนมากขึ้นซึ่งประกอบด้วยสามเหลี่ยมหลายอันคุณต้องการให้ปกติที่หนึ่งจุดยอดขึ้นอยู่กับสามเหลี่ยมอื่น ๆ ของเรขาคณิตโดยรอบและไม่ใช่แค่ปกติของสามเหลี่ยมที่อยู่ในนั้น

นี่คือตัวอย่างของรูปทรงเรขาคณิตเดียวกันกับ:

  • ทางด้านซ้ายของแต่ละบรรทัดยอด - ดังนั้นยอดสุดยอดของใบหน้ามีความแตกต่างปกติ (สำหรับทรงกลมนี้มันหมายความว่าทุกบรรทัดฐานชี้ออกมาจากจุดศูนย์กลาง) และ
  • ทางด้านขวาของบรรทัดฐานต่อหน้า - แต่ละจุดยอดจะได้รับค่าปกติเหมือนกัน (เนื่องจากคุณระบุเพียงครั้งเดียวหรือเพราะมันใช้ค่าปกติเริ่มต้นเป็น(0,0,1))

บรรทัดฐานต่อจุดสุดยอดทางด้านซ้ายบรรทัดฐานต่อหน้าทางขวา

โมเดลเฉดสีเริ่มต้น ( GL_SMOOTH) ทำให้บรรทัดฐานของจุดยอดของใบหน้าถูกแก้ไขทั่วใบหน้า สำหรับบรรทัดฐานต่อจุดสุดยอดนี้ส่งผลในรูปทรงกลมสีเทาเรียบ แต่สำหรับบรรทัดฐานต่อหน้าคุณจบลงด้วยลักษณะใบหน้าเหลี่ยมเพชรพลอย


4

ไม่ GL ไม่ได้คำนวณบรรทัดฐานสำหรับคุณ ไม่ทราบว่าปกติควรเป็นอะไร วิธีดั้งเดิมของคุณไม่ได้มีความหมายอะไรนอกเหนือจากการทำให้เป็นแรสเตอร์ (และที่อื่น ๆ สองสามแห่ง) GL ไม่ทราบว่าใบหน้าใด (สามเหลี่ยม) แบ่งปันจุดสุดยอด (การคำนวณสิ่งนี้ที่รันไทม์มีราคาแพงมากโดยไม่ใช้โครงสร้างข้อมูลที่แตกต่างกัน)

คุณต้องประมวลผล "ซุปสามเหลี่ยม" ล่วงหน้าของตาข่ายเพื่อค้นหาจุดยอดนิยมทั่วไปและคำนวณบรรทัดฐาน ควรทำก่อนที่เกมจะรันด้วยความเร็ว

นอกจากนี้ยังมีบางกรณีเช่นขอบแข็งที่มีจุดยอดทางเรขาคณิตร่วมกันที่มุม แต่คุณต้องการบรรทัดฐานที่แยกต่างหากเพื่อให้แสงสว่างอย่างถูกต้อง ในรูปแบบการเรนเดอร์ GL / D3D คุณต้องมีจุดยอดแยกสองจุด (ที่ตำแหน่งเดียวกัน) สำหรับสิ่งนี้โดยแต่ละแบบแยกกัน

คุณจะต้องใช้ทั้งหมดนี้สำหรับ UV และการทำแผนที่พื้นผิวด้วย


3

คำตอบสั้น ๆ คือคุณไม่จำเป็นต้องตั้งค่าปกติเลย เวกเตอร์ปกติจะใช้เฉพาะในกรณีที่คุณใช้แสง OpenGL (หรืออาจเป็นการคำนวณอื่น ๆ ที่อาจขึ้นอยู่กับพวกเขา) แต่ถ้ามันไม่เหมาะกับคุณ (หรือถ้าคุณใช้วิธีอื่นในการให้แสงเช่น lightmaps ) จากนั้นคุณสามารถโทรออก glNormal ทั้งหมดได้อย่างมีความสุข

ดังนั้นสมมติว่าคุณกำลังทำอะไรบางอย่างที่ระบุ glNormal เข้าท่าและดูมันมากกว่านี้

สามารถระบุ glNormal ต่อแบบดั้งเดิมหรือต่อจุดสุดยอด ด้วยบรรทัดฐานดั้งเดิมสำหรับทุกสิ่งที่มันหมายถึงว่าบรรทัดฐานทั้งหมดสำหรับแต่ละจุดสุดยอดในใบหน้าดั้งเดิมในทิศทางเดียวกัน บางครั้งนี่คือสิ่งที่คุณต้องการ แต่บางครั้งมันก็ไม่ - ที่ซึ่งบรรทัดฐานต่อจุดสุดยอดที่เป็นประโยชน์คือพวกเขาช่วยให้แต่ละจุดสุดยอดของมันได้เผชิญ

นำตัวอย่างคลาสสิกของทรงกลม หากสามเหลี่ยมแต่ละอันในทรงกลมมีปกติเหมือนกันผลลัพธ์จะเป็นเหลี่ยมมุมค่อนข้างมาก นั่นอาจไม่ใช่สิ่งที่คุณต้องการคุณต้องการแรเงาที่ราบรื่นแทน ดังนั้นสิ่งที่คุณทำก็คือค่าเฉลี่ยบรรทัดฐานสำหรับสามเหลี่ยมแต่ละรูปแบบที่ใช้จุดยอดทั่วไปและได้ผลลัพธ์เงาที่ราบรื่น


2

ตัวอย่างน้อยที่สุดที่แสดงรายละเอียดบางอย่างเกี่ยวกับวิธีการglNormalทำงานกับฟ้าผ่าแบบกระจาย

ความคิดเห็นของdisplayฟังก์ชันอธิบายความหมายของรูปสามเหลี่ยมแต่ละอัน

ป้อนคำอธิบายรูปภาพที่นี่

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

ทฤษฎี

ใน OpenGL แต่ละจุดยอดมีเวกเตอร์ปกติของตัวเองที่เกี่ยวข้อง

เวกเตอร์ปกติจะกำหนดความสว่างของจุดสุดยอดซึ่งจะถูกใช้เพื่อกำหนดความสว่างของรูปสามเหลี่ยม

เมื่อพื้นผิวตั้งฉากกับแสงจะสว่างกว่าผิวขนาน

glNormal ตั้งค่าเวกเตอร์ปกติปัจจุบันซึ่งใช้สำหรับจุดยอดต่อไปนี้ทั้งหมด

ค่าเริ่มต้นสำหรับปกติก่อนเราทุกคนglNormalคือ0,0,1คือ

เวกเตอร์ปกติจะต้องมีบรรทัดฐาน 1 ไม่เช่นนั้นสีจะเปลี่ยน! glScaleยังเปลี่ยนความยาวของบรรทัดฐาน! glEnable(GL_NORMALIZE);ทำให้ OpenGL ตั้งค่ามาตรฐานโดยอัตโนมัติเป็น 1 สำหรับเราโดยอัตโนมัติ GIF นี้แสดงให้เห็นว่าสวยงาม

บรรณานุกรม:


คำตอบที่ดี แต่ทำไมการโฟกัสไปที่คำถามเก่าและเทคโนโลยีเก่า
Vaillancourt

@AlexandreVaillancourt ขอบคุณ! คำถามเก่า: ทำไมไม่ :-) เทคโนโลยีเก่า: อะไรคือวิธีที่ดีกว่าในวันนี้
Ciro Santilli 新疆改造中心法轮功六四事件

ฉันคิดว่าสมัยใหม่ OpenGL ใช้วัตถุบัฟเฟอร์แทนการระบุ glNormal
Vaillancourt

1
แท้จริงแล้ว glNormal และ glVertex และสิ่งที่คล้ายกันได้หายไปใน OpenGL ที่ทันสมัยและเลิกใช้ไปแล้วก่อนหน้านั้น ... พวกเขาเลิกใช้จริงในขณะที่คำถามนี้ถูกถามในตอนแรก

0

คุณต้องใช้ glNormal เพื่อนำคุณสมบัติบางอย่างไปใช้กับ coder ตัวอย่างเช่นคุณอาจต้องการสร้างข้อมูลมาตรฐานรักษาขอบแข็ง


4
บางทีคุณอาจต้องการปรับปรุงคำตอบของคุณสักเล็กน้อย
Bartek Banachewicz

1
หากคุณต้องการให้ฉันปรับปรุงคำตอบของฉันคุณจะต้องชี้ให้เห็นจุดอ่อน ฉันสามารถทำอย่างละเอียดกับมันแม้ว่า เมื่อ xblax เคยคิดจะคำนวณ "ปกติ" ในแบบเดียวกัน แม้จะมีความแตกต่างระหว่างบรรทัดฐานการแก้ไขสำหรับแต่ละพิกเซลเทียบกับแต่ละหน้า มาเพิ่มหัวข้ออีกสิบนิด
AB

1
บางครั้งคุณอาจต้องการรักษาขอบแข็งและทำให้โดดเด่นมากขึ้น นี่คือภาพตัวอย่างบางส่วน: titan.cs.ukzn.ac.za/opengl/opengl-d5/trant.sgi.com/opengl/… PS ขออภัยที่โพสต์ซ้ำ ฉันใหม่สำหรับอินเตอร์เฟส SE
AB

มีปุ่ม "แก้ไข"
Bartek Banachewicz

1
คุณไม่สามารถแก้ไขได้หลังจาก 5 นาที
AB
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.