OpenGL: tại sao tôi phải thiết lập bình thường với glN normal?


19

Tôi đang tìm hiểu một số điều cơ bản về OpenGL nhưng tôi tự hỏi tại sao lại có một cuộc gọi glNormalđể thiết lập các đỉnh bình thường.

Nếu tôi tạo một hình tam giác đơn giản như thế này:

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

Không nên định nghĩa các quy tắc ngầm theo kiểu nguyên thủy hình học? Nếu tôi không đặt bình thường, OpenGL sẽ tính toán nó?


7
Tôi muốn chỉ ra rằng mã này đang sử dụng đường ống hàm cố định không dùng nữa và trong OpenGL hiện đại, cả hai quy tắc và đỉnh đều chỉ là các thuộc tính đỉnh chung.
Bartek Banachewicz

4
Đừng sử dụng chế độ ngay lập tức. Nó không được dùng nữa. Tôi rất khuyên bạn nên học lập trình đồ họa 3D hiện đại .
đúng vào

3
Tôi nghĩ bình luận về việc sử dụng chế độ ngay lập tức là hoàn toàn không liên quan ở đây. Có, người ta không nên sử dụng nó, nhưng điểm của câu hỏi vẫn giống nhau bất kể đó là chế độ tức thời, mảng đỉnh, VBO, attribs chung, attribs cố định hoặc bit ofast.
Maximus Minimus

1
Lý do nó không được xác định tự động là vì nó sẽ làm cho ánh sáng trở nên rất đa hình (vì thiếu một từ tốt hơn). Ý tôi là, tất cả các đỉnh trên một tam giác sẽ có cùng giá trị bình thường. Đối với các bề mặt phẳng, đây là những gì chúng ta mong đợi, tuy nhiên nếu bạn có mô hình khuôn mặt của một người, thì mỗi tam giác trên mặt sẽ có cùng giá trị ánh sáng (giúp bạn dễ dàng nhìn thấy các đa giác). Bằng cách chỉ định các giá trị bình thường của riêng bạn, bạn có thể làm cho mọi thứ trông mượt mà hơn (thông thường bạn tìm thấy một giá trị bình thường bằng cách lấy trung bình các giá trị bình thường của tất cả các tam giác chứa đỉnh hiện tại).
Benjamin Danger Johnson

Câu trả lời:


30

Gọi glNormalnhiều lần giữa glBegin/Endcho phép bạn đặt bình thường trên mỗi đỉnh thay vì theo nguyên thủy.

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

Đối với một lưới phức tạp hơn bao gồm nhiều tam giác, bạn sẽ muốn bình thường ở một đỉnh phụ thuộc vào các tam giác khác của hình học xung quanh và không chỉ là bình thường của tam giác mà nó thuộc về.

Dưới đây là một ví dụ về hình học tương tự với:

  • trên các quy tắc trên mỗi đỉnh bên trái - vì vậy mỗi đỉnh của một mặt có một mức bình thường khác nhau (đối với hình cầu này, nó có nghĩa là tất cả các quy tắc đều hướng ra ngoài từ tâm của nó) và
  • trên các quy tắc trên mỗi mặt phải - mỗi đỉnh có cùng giá trị bình thường (vì bạn chỉ định nó một lần hoặc vì nó sử dụng giá trị bình thường mặc định là (0,0,1)):

bình thường trên đỉnh ở bên trái, bình thường trên mỗi mặt ở bên phải

Mô hình bóng mặc định ( GL_SMOOTH) làm cho các quy tắc của các đỉnh của khuôn mặt được nội suy trên khuôn mặt. Đối với các quy tắc trên mỗi đỉnh, điều này dẫn đến một hình cầu được tô bóng mịn, nhưng đối với các quy tắc trên mỗi mặt, bạn kết thúc với giao diện mặt đặc trưng.


4

Không, GL không tính toán một chỉ tiêu cho bạn. Nó không biết những gì bình thường nên được. Người nguyên thủy của bạn không có ý nghĩa gì ngoài việc rasterization (và một vài nơi khác). GL không biết mặt nào (hình tam giác) chia sẻ một đỉnh (tính toán này khi chạy rất tốn kém mà không sử dụng các cấu trúc dữ liệu khác nhau).

Bạn cần xử lý trước "súp tam giác" của lưới để tìm các đỉnh chung và tính toán các quy tắc. Điều này nên được thực hiện trước khi trò chơi chạy cho tốc độ.

Cũng có những trường hợp như các cạnh cứng trong đó về mặt hình học có một đỉnh được chia sẻ ở góc, nhưng bạn muốn các quy tắc riêng biệt để nó sáng chính xác. Trong mô hình kết xuất GL / D3D, bạn cần hai đỉnh riêng biệt (tại cùng một vị trí) cho điều này, mỗi đỉnh có một bình thường riêng.

Bạn cũng sẽ cần tất cả điều này cho UV và ánh xạ kết cấu.


3

Câu trả lời ngắn gọn là bạn thực sự không cần phải thiết lập bình thường. Các vectơ bình thường chỉ được sử dụng nếu bạn đang thực hiện chiếu sáng OpenGL (hoặc có thể là một số phép tính khác có thể phụ thuộc vào chúng), nhưng nếu điều đó không áp dụng cho bạn (hoặc nếu bạn đang sử dụng một phương pháp khác để thực hiện chiếu sáng, ví dụ như lightmap ) sau đó bạn có thể khá vui vẻ bỏ qua tất cả các cuộc gọi glN normal.

Vì vậy, hãy giả sử rằng bạn đang làm một cái gì đó trong đó chỉ định glN normal có ý nghĩa và xem xét nó thêm một số.

glN normal có thể được chỉ định theo nguyên thủy hoặc mỗi đỉnh. Với các quy tắc theo nguyên thủy, tất cả những gì nó có nghĩa là tất cả các quy tắc cho mỗi đỉnh trong mặt nguyên thủy theo cùng một hướng. Đôi khi đây là những gì bạn muốn nhưng đôi khi không - trong đó các quy tắc trên mỗi đỉnh có ích là chúng cho phép mỗi đỉnh có mặt của nó.

Lấy ví dụ kinh điển về một quả cầu. Nếu mỗi tam giác trong một hình cầu có cùng mức bình thường thì kết quả cuối cùng sẽ khá rõ ràng. Đó có lẽ không phải là những gì bạn muốn, thay vào đó bạn muốn có một bóng mượt. Vì vậy, những gì bạn làm là trung bình các quy tắc cho mỗi tam giác có chung một đỉnh chung và có được kết quả bóng mờ mượt mà.


2

Ví dụ tối thiểu minh họa một số chi tiết về cách làm glNormalviệc với sét khuếch tán.

Các ý kiến ​​của displayhàm giải thích ý nghĩa của mỗi tam giác.

nhập mô tả hình ảnh ở đây

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

Học thuyết

Trong OpenGL, mỗi đỉnh có vectơ bình thường liên quan.

Vectơ bình thường xác định độ sáng của đỉnh, sau đó được sử dụng để xác định độ sáng của tam giác.

Khi một bề mặt vuông góc với ánh sáng, nó sáng hơn bề mặt song song.

glNormal thiết lập vectơ bình thường hiện tại, được sử dụng cho tất cả các đỉnh sau.

Giá trị ban đầu cho bình thường trước khi tất cả chúng ta glNormal0,0,1.

Các vectơ bình thường phải có định mức 1, nếu không màu thay đổi! glScalecũng thay đổi chiều dài của thông thường! glEnable(GL_NORMALIZE);làm cho OpenGL tự động đặt định mức của họ thành 1 cho chúng tôi. GIF này minh họa rất đẹp.

Tài liệu tham khảo:


Câu trả lời hay, nhưng tại sao tập trung vào một câu hỏi cũ và một công nghệ cũ?
Vaillancourt

@AlexandreVaillancourt cảm ơn! Câu hỏi cũ: tại sao không :-) Công nghệ cũ: ngày nay cách nào tốt hơn?
Ciro Santilli 心 心 事件

Tôi nghĩ OpenGL hiện đại sử dụng các đối tượng bộ đệm thay vì chỉ định glN normal.
Vaillancourt

1
Thật vậy, glN normal và glVertex và những thứ tương tự đã biến mất trong OpenGL hiện đại, và đã bị phản đối một thời gian trước đó ... thực tế chúng đã bị phản đối ngay cả khi câu hỏi này ban đầu được hỏi.

0

Bạn cần glN normal để thực hiện một số tính năng phụ thuộc vào bộ mã hóa. Ví dụ, bạn có thể muốn tạo các thông số dữ liệu bảo toàn các cạnh cứng.


4
Có lẽ bạn sẽ muốn cải thiện câu trả lời của bạn một chút.
Bartek Banachewicz

1
Nếu bạn muốn tôi cải thiện câu trả lời của mình, bạn sẽ cần chỉ ra những điểm yếu. Tôi có thể giải thích về nó mặc dù. Như xblax đã từng nghĩ, mỗi "nên" bình thường được tính theo cùng một cách. Ngay cả khi có sự khác biệt giữa các quy tắc nội suy cho từng pixel so với từng khuôn mặt. Hãy tham gia vào chủ đề nhiều hơn một chút.
AB.

1
Đôi khi bạn có thể muốn duy trì các cạnh cứng và làm cho nó đặc biệt hơn. Dưới đây là một số hình ảnh mẫu: titan.cs.ukzn.ac.za/opengl/opengl-d5/trant.sgi.com/opengl/ trên PS. Xin lỗi vì đăng bài đôi. Tôi mới sử dụng giao diện SE
AB.

có nút "chỉnh sửa".
Bartek Banachewicz

1
Bạn không thể chỉnh sửa sau 5 phút
AB.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.