1. Trang chủ
  2. » Giáo án - Bài giảng

Đồ họa máy tính: Quản lý trạng thái và vẽ các vật thể hình học

84 47 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 84
Dung lượng 2,38 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Ví dụ, để xóa cả bộ đệm màu và bộ đệm độ sâu depth buffer, bạn sẽ sử dụng những câu lệnh sau: Trong trường hợp này, lệnh gọi ​glClearColor giống như trước, lệnh ​glClearDepth chỉ định gi

Trang 1

Nhóm: 1

Thành viên:

Nguyễn Quốc An

Nguyễn Đỗ Dương

Dương Quốc Hưng

Hoàng Phương Linh

Trần Công Minh

Lâm Hà Thái

Chủ đề: State management and Drawing geometric objects

(Quản lý trạng thái và vẽ các vật thể hình học)

Phân công công việc:

Nguyễn Quốc An Lên ý tưởng slide thuyết trình

Quản lý tiến độ

Chuẩn bị slide thuyết trình Soạn lý thuyết và bài tập phần A Drawing Survival Kit Nguyễn Đỗ Dương Chuẩn bị slide thuyết trình

Soạn lý thuyết và bài tập phần Vertex Arrays Soạn lý thuyết và bài tập phần Vertex Objects Dương Quốc Hưng Chuẩn bị tài liệu

Soạn lý thuyết và bài tập phần Basic State Management Soạn lý thuyết và bài tập phần Normal Vectors

Soạn lý thuyết và bài tập phần Attribute Groups Soạn phần Bài tập ứng dụng

Hoàng Phương Linh Chuẩn bị slide thuyết trình

Soạn lý thuyết và bài tập phần Buffer Objects Trần Công Minh Chuẩn bị slide thuyết trình

Soạn lý thuyết và bài tập phần Describing Points, Lines, and Polygons Lâm Hà Thái Chuẩn bị slide thuyết trình

Soạn lý thuyết và bài tập phần Displaying Points, Lines, and Polygons

Trang 2

Mục lục

2.1 Quản lý trạng thái và vẽ các vật thể hình học ​(State management and Drawing geometric

2.1.1.3 Ép việc vẽ hoàn thành ( Forcing completion of drawing) 7

2.1.1.4 Bộ công cụ hệ tọa độ ( Coordinate system survival kit​) 9

2.1.2 Mô tả điểm, đường thẳng, đa giác (Describing points, lines polygons) 10

2.1.2.1 Điểm, đường thẳng, đa giác là gì (What are points, lines and polygons) 10

2.1.2.3 Vẽ các hình học cơ sở trong OpenGL (OpenGL geometric drawing primitives) 14 2.1.3 Quản lý trạng thái cơ bản (Basic state management) 19

2.1.4 Hiển thị điểm, đường thẳng và đa giác (Displaying points, lines and polygons) 20

2.1.4.3.1 Đa giác dưới dạng điểm, đường viền hoặc tô kín 22

2.1.7.1 Tạo Đối tượng đệm ( Creating buffer objects) 42

2.1.7.2 Làm cho đối tượng đệm hoạt động (Making a buffer object active) 43

2.1.7.3 Phân bổ và khởi tạo đối tượng đệm với dữ liệu (Allocating and initializing buffer

2.1.7.4 Cập nhật dữ liệu trong đối tượng đệm (Updating data values in buffer objects)46 2.1.7.5 Sao chép dữ liệu giữa các đối tượng đệm ( Copying data between buffer objects​)

49

2.1.7.6 Xóa đối tượng đệm (Cleaning up buffer objects) 50

Trang 3

2.1.7.7 Sử dụng đối tượng đệm với dữ liệu mảng các đỉnh (Using buffer objects with

2.2.1.2 Mô tả điểm, đường thẳng, đa giác (Describing points, lines polygons) 60

2.2.1.3 Quản lý trạng thái cơ bản (Basic state management) 60

2.2.1.4 Hiển thị điểm, đường thẳng và đa giác (Displaying points, lines and polygons) 61

2.2.1.8 Đối tượng mảng đỉnh (Vertex-array objects) 62

2.2.2.2 Mô tả điểm, đường thẳng, đa giác (Describing points, lines polygons) 63

2.2.2.3 Quản lý trạng thái cơ bản (Basic state management) 64

2.2.2.4 Hiển thị điểm, đường thẳng và đa giác (Displaying points, lines and polygons) 65

2.2.2.8 Đối tượng mảng đỉnh (Vertex-array objects) 69

2.3 Sản phẩm ứng dụng (xây dựng mô hình đa giác của bề mặt vật thể) 71

2.3.6.1 Những lưu ý khi xây dựng mô hình đa giác của bề mặt vật thể 77

Trang 4

Chương 2: State management and Drawing geometric objects

(Quản lý trạng thái và vẽ các vật thể hình học)

2.1 Quản lý trạng thái và vẽ các vật thể hình học

Chương này có các phần chính sau:

● A Drawing Survival Kit giải thích cách xóa cửa sổ và bắt buộc việc vẽ phải hoàn thành Nó

cũng cung cấp cho bạn thông tin cơ bản về kiểm soát màu sắc của các đối tượng hình học và

mô tả một hệ tọa độ ● Mô tả các điểm, đường và đa giác cho bạn biết tập hợp các đối tượng hình học cơ sở là gì

và làm sao để vẽ chúng ● Quản lý trạng thái cơ bản mô tả cách bật và tắt một số trạng thái (chế độ) và truy vấn các

biến trạng thái ● Hiển thị các điểm, đường và đa giác giải thích những điều khiển chi tiết về việc vẽ các đối

tượng cơ sở – ví dụ, đường kính nào cho các điểm, khi nào thì đường thẳng hay nét đứt và

liệu các đa giác được outlined hay filled ● Vectơ pháp tuyến thảo luận cách đặc tả các vectơ pháp tuyến cho các đối tượng hình học và

(một cách vắn tắt) những vectơ này là cho cái gì ● Mảng đỉnh ​(Vertex Arrays) chỉ cho bạn cách bỏ nhiều dữ liệu hình học vào một vài mảng và

làm sao, chỉ với một vài hàm gọi, có thể hiển thị hình dạng nó mô tả Việc giảm các lời gọi

hàm có thể làm tăng hiệu quả và hiệu suất của việc vẽ ● Các nhóm thuộc tính ​(Attribute Groups) cho thấy cách truy vấn giá trị hiện tại của biến

trạng thái và cách lưu và khôi phục một số giá trị trạng thái có liên quan cùng một lúc ● Một số gợi ý để xây dựng mô hình đa giác của bề mặt khám phá các vấn đề và kỹ thuật

liên quan đến việc xây dựng các xấp xỉ đa giác cho các bề mặt 2.1.1 A drawing survival kit Phần này giải thích cách xóa cửa sổ để chuẩn bị vẽ, thiết lập màu sắc của các đối tượng cần vẽ và

buộc sự vẽ hoàn thành Những việc đó không có liên quan gì trực tiếp đến các đối tượng hình học,

nhưng bất kỳ chương trình vẽ các đối tượng hình học nào cũng đều phải giải quyết các vấn đề đó 2.1.1.1 Xoá cửa sổ ( Cleaning the window) Vẽ trên màn hình máy tính khác với vẽ trên giấy, đó là giấy bắt đầu là màu trắng và tất cả những

gì bạn phải làm là vẽ hình ảnh Trên máy tính, bộ nhớ thường giữ ảnh cuối cùng bạn vẽ, vì vậy bạn

cần xóa nó thành màu nền trước khi bắt đầu vẽ cảnh mới Màu bạn sử dụng cho nền phụ thuộc vào

mỗi ứng dụng Đối với một trình xử lý văn bản, bạn có thể xóa với màu trắng (màu của tờ giấy)

trước khi bắt đầu vẽ chữ Nếu bạn đang vẽ một khung cảnh từ một tàu vũ trụ, bạn xóa với màu đen

của không gian trước khi bắt đầu vẽ các ngôi sao, hành tinh, và phi thuyền Đôi khi bạn có thể

không cần phải xóa màn hình; ví dụ, nếu hình ảnh ở bên trong một căn phòng, toàn bộ cửa sổ đồ

họa sẽ bị che phủ khi bạn vẽ lên tất cả các bức tường Lúc này, bạn có thể tự hỏi tại sao chúng ta tiếp tục nói về việc xóa cửa sổ – tại sao không chỉ vẽ

một hình chữ nhật có màu thích hợp đủ lớn để che toàn bộ cửa sổ? Đầu tiên, một lệnh để xóa một

Trang 5

cửa sổ có thể hiệu quả hơn nhiều so với một lệnh vẽ Ngoài ra, như bạn sẽ thấy trong Chương 3, OpenGL cho phép bạn thiết lập hệ tọa độ, vị trí xem và hướng xem tùy ý, do đó khó có thể tìm ra kích thước và vị trí thích hợp cho hình chữ nhật xóa cửa sổ Cuối cùng, trên nhiều máy, phần cứng

đồ họa bao gồm nhiều bộ đệm khác ngoài bộ đệm chứa các màu của các pixel được hiển thị Những

bộ đệm khác phải được xóa theo thời gian, và sẽ thuận tiện khi có một lệnh xóa duy nhất có thể xóa bất kỳ sự kết hợp của chúng (Xem Chương 10 để thảo luận về tất cả các bộ đệm.)

Bạn cũng phải biết cách các màu của pixel được lưu trữ trong phần cứng đồ họa được gọi là bitplanes Có hai phương pháp lưu trữ Giá trị màu đỏ, lục, lam và alpha (RGBA) của pixel có thể được lưu trữ trực tiếp trong bitplanes hoặc một giá trị chỉ mục duy nhất (single index value) tham chiếu đến bảng tra cứu màu (color lookup table) được lưu trữ Chế độ hiển thị màu RGBA thường được sử dụng, vì vậy hầu hết các ví dụ trong cuốn sách này đều sử dụng nó (Xem Chương 4 để biết thêm thông tin về cả hai chế độ hiển thị.) Bạn có thể bỏ qua tất cả các tham chiếu đến các giá trị alpha cho đến Chương 6

Ví dụ các dòng mã này xóa cửa sổ chế độ RGBA thành màu đen:

Dòng đầu tiên thiết lập màu xóa thành màu đen và dòng tiếp theo xóa toàn bộ cửa sổ sang màu xóa hiện hành Tham số duy nhất trong hàm ​glClear() cho biết bộ đệm nào sẽ bị xóa Trong trường hợp này, chương trình chỉ xóa bộ đệm màu, nơi ảnh được hiển thị trên màn hình đang được giữ Thông thường, bạn thiết lập màu xóa một lần, khi bắt đầu ứng dụng, và sau đó bạn xóa bộ đệm thường xuyên khi cần thiết OpenGL giữ màu xóa hiện hành dưới dạng biến trạng thái thay vì yêu cầu bạn thiết lập nó mỗi khi bộ đệm bị xóa

Chương 4 và Chương 10 nói về cách sử dụng các bộ đệm khác Hiện giờ, tất cả những gì bạn cần biết là việc xóa chúng rất đơn giản Ví dụ, để xóa cả bộ đệm màu và bộ đệm độ sâu (depth buffer), bạn sẽ sử dụng những câu lệnh sau:

Trong trường hợp này, lệnh gọi ​glClearColor() giống như trước, lệnh ​glClearDepth() chỉ

định giá trị mà mọi pixel của bộ đệm độ sâu sẽ được đặt và tham số của lệnh ​glClear() bây giờ

bao gồm logic OR theo bit của tất cả các bộ đệm được xóa Bản tóm tắt sau đây của ​glClear()

bao gồm một bảng liệt kê các bộ đệm có thể được xóa, tên của chúng và chương thảo luận về từng loại bộ đệm

void ​glClearColor​(GLclampf ​red​, GLclampf ​green​, GLclampf ​blue​, GLclampf ​alpha​);

Đặt màu xóa hiện hành để sử dụng trong việc xóa bộ đệm màu trong chế độ RGBA (Xem Chương 4 để biết thêm thông tin về chế độ RGBA.) Các giá trị màu đỏ, lục, lam và alpha nằm trong khoảng [0,1] Màu xóa mặc định là (0, 0, 0, 0), tức là màu đen

void ​glClear​(GLbitfield ​mask​);

Xóa các bộ đệm được chỉ định về giá trị xóa hiện tại của chúng Mặt nạ đối số là tổ hợp logic

OR theo bit của các giá trị được liệt kê trong Bảng 2.1.1.1

Trang 6

Bảng 2.1.1.1: Clearing Buffers

Trước khi phát đi một lệnh ​glClear() để xóa các bộ đệm, bạn phải thiết lập các giá trị mà mỗi

bộ đệm sẽ bị xóa nếu bạn muốn nó khác với giá trị mặc định ● glClearColor​() ​: thiết lập giá trị để xóa bộ đệm màu (RGBA color) ● glClearDepth()​ ​: thiết lập các giá trị để xóa bộ đệm độ sâu (depth buffer) ● glClearIndex()​ ​: thiết lập giá trị color index ● glClearAccum()​ : thiết lập giá trị accumulation color ● glClearStencil()​ : thiết lập giá trị stencil được sử dụng để xóa bộ đệm tương ứng (Xem Chương 4 và Chương 10 để biết mô tả về các bộ đệm này và cách sử dụng chúng) OpenGL cho phép bạn chỉ định nhiều bộ đệm vì việc xóa thường là mất nhiều thời gian, vì mọi

pixel trong cửa sổ (có thể là hàng triệu) đều được đụng đến và một vài phần cứng đồ họa cho phép

bộ các bộ đệm được xóa đồng thời Phần cứng mà không hỗ trợ xóa đồng thời thì thực hiện xóa tuần tự Sự khác biệt giữa: glClear​(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear​(GL_COLOR_BUFFER_BIT); glClear​(GL_DEPTH_BUFFER_BIT); đó là, mặc dù cả hai đều có cùng kết quả nhưng cái đầu sẽ chạy nhanh hơn trên nhiều máy Chắc

chắn nó không chạy chậm hơn 2.1.1.2 Đặc tả màu ( Specify a color) Với OpenGL, miêu tả hình dạng của một đối tượng được vẽ độc lập với miêu tả về màu sắc của

nó Bất cứ khi nào một đối tượng được vẽ, nó được vẽ bằng lược đồ màu được đặc tả hiện hành

Lược đồ màu có thể đơn giản như “vẽ mọi thứ đỏ như máy bơm nước cứu hỏa” hoặc có thể phức

tạp như “giả sử đối tượng được làm bằng nhựa màu xanh lam, có một ánh đèn màu vàng chỉ hướng

đó, hướng đó, và có một ánh sáng màu đỏ nâu chung chung ở mọi nơi khác.” Nói chung, một lập

trình viên OpenGL đầu tiên đặt màu hoặc lược đồ màu, sau đó vẽ các đối tượng Cho đến khi lược

đồ màu hoặc màu được thay đổi, tất cả các đối tượng sẽ được vẽ bằng màu đó hoặc sử dụng lược đồ

màu đó Phương pháp này giúp OpenGL đạt được hiệu suất vẽ cao hơn so với kết quả nếu nó không lưu vết màu hiện tại

Ví dụ:

Color buffer GL_COLOR_BUFFER_BIT Chapter 4

Depth buffer GL_DEPTH_BUFFER_BIT Chapter 10

Accumulation buffer GL_ACCUM_BUFFER_BIT Chapter 10

Stencil buffer GL_STENCIL_BUFFER_BIT Chapter 10

set_current_color(red);

Trang 7

sẽ vẽ đối tượng A và B màu đỏ và đối tượng C có màu xanh lam Lệnh trên dòng thứ tư đặt màu

hiện tại thành màu xanh lá cây là vô ích Tô màu (​coloring​), chiếu sáng (​lighting​) và đổ bóng (​shading​) là các chủ đề lớn mà phải dành

toàn bộ các chương hoặc ​sections ​lớn dành cho nó Để vẽ các đối tượng hình học cơ sở có thể nhìn

thấy được, tuy nhiên, bạn cần một số kiến thức cơ bản về cách thiết lập màu hiện hành; thông tin

này được cung cấp trong các đoạn tiếp theo (Xem Chương 4 và Chương 5 để biết chi tiết về các

chủ đề này.) Để thiết lập một màu, sử dụng lệnh ​glColor3f​()​ Nó lấy ba tham số, tất cả đều là các số chấm

động nằm trong khoảng từ 0.0 đến 1.0 Các tham số, theo thứ tự, là các thành phần (​component​ )

màu đỏ, lục và lam của màu Bạn có thể nghĩ về ba giá trị này như là đặc tả một “kết hợp” của màu

sắc: 0.0 có nghĩa là không sử dụng bất kỳ thành phần nào, và 1.0 có nghĩa là sử dụng tất cả của

thành phần đó Vì vậy, lệnh ​glColor3f​(1.0, 0.0, 0.0) làm cho màu đỏ sáng nhất mà hệ

thống có thể vẽ, không có thành phần màu lục hoặc màu lam Nếu tất cả các số là 0 tạo thành màu

đen; ngược lại, tất cả là 1 làm thành màu trắng Đặt tất cả ba thành phần thành 0,5 thành màu xám

(ở giữa màu đen và màu trắng) Dưới đây là tám lệnh và màu sắc mà chúng sẽ thiết lập glColor3f​(0.0, 0.0, 0.0); /* black */ glColor3f​(1.0, 0.0, 0.0); /* red */ glColor3f​(0.0, 1.0, 0.0); /* green */ glColor3f​(1.0, 1.0, 0.0); /* yellow */ glColor3f​(0.0, 0.0, 1.0); /* blue */ glColor3f​(1.0, 0.0, 1.0); /* magenta */ glColor3f​(0.0, 1.0, 1.0); /* cyan */ glColor3f​(1.0, 1.0, 1.0); /* white */ Bạn có thể đã nhận thấy trước đó rằng lệnh để thiết lập màu xóa, ​glClearColor()​, có bốn tham

số, ba tham số đầu tiên ứng với các tham số cho ​glColor3f()​ Tham số thứ tư là giá trị alpha; nó

được đề cập chi tiết trong “Blending” trong Chương 6 Hiện tại, hãy thiết lập tham số thứ tư của

glClearColor()​ thành 0.0, là giá trị mặc định của nó 2.1.1.3 Ép việc vẽ hoàn thành ( Forcing completion of drawing​) Như bạn đã thấy trong “OpenGL Rendering Pipeline” trong Chương 1, hầu hết các hệ thống đồ

họa hiện đại có thể được coi là ​pipeline​ Bộ xử lý trung tâm chính (CPU) đưa ra lệnh vẽ Có lẽ phần

cứng khác thực hiện chuyển đổi hình học ​Clipping ​được thực hiện, tiếp theo là tô bóng và/hoặc

texturing Cuối cùng, các giá trị được ghi vào bitplanes để hiển thị Trong các kiến trúc cao cấp,

mỗi hoạt động này được thực hiện bởi một phần cứng khác nhau được thiết kế để thực hiện nhiệm

vụ cụ thể của nó một cách nhanh chóng Trong một kiến trúc như vậy, không cần CPU phải đợi cho

mỗi lệnh vẽ để hoàn tất trước khi phát hành lệnh tiếp theo Trong khi CPU đang gửi một vertex

draw_object(A);

draw_object(B);

set_current_color(green);

set_current_color(blue);

draw_object(C);

Trang 8

xuống ​pipeline​, phần cứng chuyển đổi đang làm việc trên vertex cuối cùng được gửi đi, cái trước đó

đang bị cắt bớt, và cứ thế Trong một hệ thống như vậy, nếu CPU chờ cho mỗi lệnh hoàn thành

trước khi phát ra lệnh tiếp theo, có thể làm ảnh hưởng hiệu suất rất lớn Ngoài ra, ứng dụng có thể đang chạy trên nhiều máy Ví dụ: giả sử rằng chương trình chính đang

chạy ở nơi khác (trên một máy được gọi là máy khách) và bạn đang xem kết quả của bản vẽ trên

máy trạm hoặc thiết bị đầu cuối (máy chủ), được kết nối bằng mạng với máy khách Trong trường

hợp đó, việc gửi từng lệnh qua mạng cùng một lúc có thể không hiệu quả vì chi phí đáng kể thường

liên quan đến mỗi lần truyền mạng Thông thường, máy khách tập hợp một tập hợp các lệnh thành một gói mạng duy nhất trước khi gửi đi Thật không may, mã mạng trên máy khách thường không

có cách nào để biết rằng chương trình đồ họa đã vẽ xong một khung hoặc cảnh Trong trường hợp

xấu nhất, nó sẽ đợi mãi cho đủ các lệnh vẽ bổ sung để lấp đầy một gói và bạn sẽ không bao giờ nhìn

thấy bản vẽ hoàn chỉnh Vì lý do này, OpenGL cung cấp lệnh ​glFlush()​, buộc máy khách gửi gói mạng mặc dù nó có

thể không đầy Khi không có mạng và tất cả các lệnh được thực thi ngay lập tức trên máy chủ,

glFlush() có thể không có tác dụng Tuy nhiên, nếu bạn đang viết một chương trình mà bạn muốn hoạt động bình thường cả khi có và không có mạng, hãy thêm lệnh gọi hàm ​glFlush() ở

cuối mỗi khung hình hoặc cảnh Lưu ý rằng ​glFlush() không đợi bản vẽ hoàn thành – nó chỉ

buộc việc vẽ bắt đầu thực thi, do đó đảm bảo rằng tất cả các lệnh trước đó thực thi trong thời gian

hữu hạn ngay cả khi không có lệnh vẽ nào được thực hiện Có những tình huống khác mà glFlush()​ hữu ích: ● Software renderers tạo hình ảnh trong bộ nhớ hệ thống và không muốn cập nhật liên tục màn

hình ● Triển khai tập hợp các bộ lệnh kết xuất để khấu hao chi phí khởi động Ví dụ về truyền mạng

nói trên là một ví dụ về điều này Lệnh ​void ​glFlush​(void) buộc các lệnh OpenGL được phát hành trước đó để bắt đầu thực

hiện, do đó đảm bảo rằng chúng hoàn thành trong thời gian hữu hạn Một vài lệnh — ví dụ, các lệnh hoán đổi bộ đệm trong chế độ bộ đệm kép — tự động chuyển các

lệnh đang chờ xử lý vào mạng trước khi chúng có thể xảy ra Nếu ​glFlush() không đủ cho bạn, hãy thử ​glFinish()​ Lệnh này xóa mạng như ​glFlush()

thực hiện và sau đó đợi thông báo từ phần cứng đồ họa hoặc mạng cho biết rằng bản vẽ đã hoàn tất

trong bộ đệm khung Bạn có thể cần sử dụng ​glFinish() nếu bạn muốn đồng bộ hóa các tác vụ -

ví dụ: để đảm bảo rằng kết xuất ba chiều của bạn hiển thị trên màn hình trước khi bạn sử dụng

Display PostScrip​t để vẽ các nhãn trên đầu hiển thị Một ví dụ khác là đảm bảo rằng bản vẽ đã hoàn

thành trước khi nó bắt đầu chấp nhận đầu vào của người dùng Sau khi bạn đưa ra lệnh

glFinish()​, tiến trình đồ họa của bạn sẽ bị chặn cho đến khi nó nhận được thông báo từ phần

cứng đồ họa rằng bản vẽ đã hoàn tất Hãy nhớ rằng việc sử dụng quá nhiều ​glFinish() có thể làm

giảm hiệu suất của ứng dụng, đặc biệt nếu bạn đang chạy qua mạng, vì nó yêu cầu liên lạc khứ hồi

Nếu glFlush()​ là đủ cho nhu cầu của bạn, hãy sử dụng nó thay vì glFinish()​

void ​glFinish​(void);

Trang 9

Bắt buộc hoàn thành tất cả các lệnh OpenGL đã phát hành trước đó Lệnh này không trở lại cho đến khi tất cả các hiệu ứng từ các lệnh trước đó được thực hiện đầy đủ

2.1.1.4 Bộ công cụ hệ tọa độ ( Coordinate system survival kit)

Bất cứ khi nào bạn mở một cửa sổ ban đầu hoặc sau đó di chuyển hoặc thay đổi kích thước cửa sổ

đó, hệ thống cửa sổ sẽ gửi một sự kiện để thông báo cho bạn Nếu bạn đang sử dụng GLUT, thông

báo sẽ tự động; bất kỳ quy trình nào đã được đăng ký với glutReshapeFunc() sẽ được gọi Bạn phải đăng ký một chức năng gọi lại mà:

● Thiết lập lại vùng hình chữ nhật mà là khung để vẽ mới

● Xác định hệ tọa độ mà đối tượng sẽ được vẽ

Trong Chương 3, bạn sẽ thấy cách xác định tọa độ ba chiều nhưng ngay bây giờ chỉ cần tạo một hai chiều đơn giản, cơ bản hệ tọa độ mà bạn có thể vẽ một vài đối tượng Gọi

glutReshapeFunc(reshape)​, trong đó ​reshape()​ là hàm được hiển thị trong ví dụ dưới đây

​Kernel ​của GLUT sẽ truyền cho hàm này hai đối số: chiều rộng và chiều cao, tính bằng pixel, của cửa sổ mới, đã di chuyển hoặc đã thay đổi kích thước ​glViewport() điều chỉnh hình chữ nhật

pixel để vẽ thành toàn bộ cửa sổ mới Ba quy trình tiếp theo điều chỉnh hệ tọa độ để vẽ sao cho góc dưới bên trái là (0, 0) và góc trên bên phải là (w, h) (xem Hình 2-1)

Để giải thích nó theo cách khác, hãy nghĩ về một mảnh giấy vẽ đồ thị Các giá trị w và h trong

reshape() ​biểu thị số lượng cột và hàng hình vuông trên giấy vẽ đồ thị của bạn Sau đó, bạn phải đặt các trục trên giấy kẻ ô vuông Quy trình ​gluOrtho2D() ​đặt gốc, (0, 0), ở hình vuông thấp nhất, ngoài cùng bên trái và làm cho mỗi hình vuông đại diện cho một đơn vị Bây giờ, khi bạn hiển thị các điểm, đường thẳng và đa giác trong phần còn lại của chương này, chúng sẽ xuất hiện trên trang giấy này dưới dạng hình vuông dễ đoán (Hiện tại, hãy giữ cho tất cả các đối tượng của bạn luôn giống nhau.)

void​ ​reshape​(​int​ w, ​int​ h) {

glViewport(0, 0, (GLsizei) w, (GLsizei) h);

glMatrixMode(GL_PROJECTION);

glLoadIdentity();

gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h);

}

Trang 10

Hình 2.1.1.1:​ Hệ thống tọa độ với w = 50, h = 50

2.1.2 Mô tả điểm, đường thẳng, đa giác (Describing points, lines polygons)

Giải thích cách mô tả các hình học cơ bản trong OpenGL Tất cả các hình học cơ bản cuối cùng

được mô tả dưới dạng các đỉnh và tọa độ xác định chính các điểm của đó , các điểm cuối của đoạn

thẳng hoặc các góc của đa giác Phần tiếp theo thảo luận về cách những thứ cơ bản được hiển thị và

bạn có quyền kiểm soát gì đối với màn hình của chúng 2.1.2.1 Điểm, đường thẳng, đa giác là gì (What are points, lines and polygons) Bạn có thể có một ý tưởng khá tốt về ý nghĩa của một nhà toán học khi điều kiện điểm, đường

thẳng và đa giác Các ý nghĩa của OpenGL tương tự nhau, nhưng không khá giống nhau Một sự khác biệt đến từ những hạn chế của tính toán dựa trên máy tính.Trong bất kỳ triển khai

OpenGL nào, các phép tính dấu phẩy động là hữu hạn độ chính xác, và chúng có sai số hoàn chỉnh

Do đó, tọa độ của Các điểm, đường và đa giác trong OpenGL cũng gặp phải vấn đề tương tự Một sự khác biệt quan trọng hơn phát sinh từ những hạn chế của đồ họa raster trưng bày Trên

màn hình như vậy, đơn vị nhỏ nhất có thể hiển thị là pixel và mặc dù pixel có thể rộng dưới 1/100

inch nhưng chúng vẫn lớn hơn nhiều so với các khái niệm của nhà toán học về vô hạn nhỏ (đối với

điểm) và mỏng vô hạn (đối với đường) Khi OpenGL thực hiện các phép tính, nó giả định rằng các

điểm được biểu diễn dưới dạng vectơ của số dấu phẩy động Tuy nhiên, một điểm thường (nhưng

không phải luôn luôn) được vẽ dưới dạng một pixel duy nhất và nhiều điểm khác nhau với các tọa

độ hơi khác nhau có thể được vẽ bởi OpenGL trên cùng một pixel ● Điểm (Points) Một điểm được biểu diễn bởi một tập hợp các số dấu phẩy động được gọi là một đỉnh Tất cả

tính toán bên trong được thực hiện như thể các đỉnh là ba chiều Dọc được người dùng chỉ

định là hai chiều (nghĩa là chỉ có tọa độ x và y) được OpenGL gán một tọa độ z bằng 0 ● Đường thẳng (Lines) Trong OpenGL, thuật ngữ đường đề cập đến một đoạn thẳng, không phải phiên bản của nhà

toán học mở rộng đến vô cùng theo cả hai hướng Có nhiều cách dễ dàng để chỉ định một chuỗi các đoạn thẳng được kết nối, hoặc thậm chí một chuỗi các đoạn khép kín, được kết nối

Trang 11

(xem ​Hình 2.1.2.1​) Tuy nhiên, trong mọi trường hợp, các đường tạo thành chuỗi được kết

nối được xác định theo các đỉnh tại điểm cuối của chúng

Hình 2.1.2.1:​ ​Hai chuỗi đoạn đường được kết nối

● Đa giác (Polygons)

Đa giác là các khu vực được bao bởi các vòng khép kín của các đoạn thẳng, trong đó các

đoạn thẳng được xác định bởi các đỉnh tại điểm cuối của chúng Đa giác thường được vẽ với

các pixel bên trong được điền vào, nhưng bạn cũng có thể vẽ chúng dưới dạng đường viền

hoặc tập hợp các điểm (Xem “Chi tiết Đa giác” trên trang 60.) Nói chung, đa giác có thể

phức tạp, vì vậy OpenGL áp đặt một số hạn chế mạnh mẽ đối với những gì tạo thành một đa

giác hình học cơ sở Đầu tiên, các cạnh của đa giác OpenGL không thể cắt nhau (một nhà

toán học sẽ gọi một đa giác thỏa mãn điều kiện này là một đa giác đơn giản) Thứ hai, các đa

giác OpenGL phải lồi, nghĩa là chúng không thể có các vết lõm Nói một cách chính xác,

một vùng là lồi nếu, cho bất kỳ hai điểm nào ở bên trong, đoạn thẳng nối chúng cũng ở bên

trong Xem ​Hình 2.1.2.2 để biết một số ví dụ về đa giác hợp lệ và không hợp lệ Tuy nhiên,

OpenGL không hạn chế số lượng đoạn thẳng tạo nên ranh giới của một đa giác lồi Lưu ý

rằng không thể mô tả đa giác có lỗ Chúng không lồi và chúng không thể được vẽ với một

ranh giới tạo thành một vòng khép kín​ Cần biết rằng nếu bạn trình bày OpenGL với một đa

giác không lồi, nó có thể không vẽ nó như bạn mong đợi Ví dụ, trên hầu hết các hệ thống,

không nhiều hơn vỏ lồi của đa giác sẽ được lấp đầy Trên một số hệ thống, ít hơn thân tàu lồi

có thể được lấp đầy Hình 2.1.2.2:​ Đa giác hợp lệ và không hợp lệ Lý do cho các hạn chế của OpenGL đối với các loại đa giác hợp lệ là việc cung cấp phần cứng

kết xuất đa giác nhanh chóng cho lớp đa giác bị hạn chế đó sẽ đơn giản hơn Đa giác đơn giản có

thể được kết xuất nhanh chóng Các trường hợp khó rất khó được phát hiện nhanh chóng, vì vậy để đạt hiệu suất tối đa, OpenGL vượt qua các ngón tay của mình và giả định các đa giác là đơn giản

Trang 12

Nhiều bề mặt trong thế giới thực bao gồm đa giác không đơn giản, đa giác không lồi hoặc đa giác

có lỗ Vì tất cả các đa giác như vậy có thể được hình thành từ sự kết hợp của các đa giác lồi đơn giản, một số quy trình để xây dựng các đối tượng phức tạp hơn được cung cấp trong thư viện GLU Những quy trình này có những mô tả phức tạp và cắt bỏ chúng, hoặc chia chúng thành các nhóm đa giác OpenGL đơn giản hơn mà sau đó có thể được hiển thị

Vì các đỉnh OpenGL luôn là ba chiều, các điểm tạo thành ranh giới của một đa giác cụ thể không nhất thiết phải nằm trên cùng một mặt phẳng trong không gian (Tất nhiên, chúng đúng trong nhiềutrường hợp - ví dụ: nếu tất cả các tọa độ z đều bằng 0 hoặc nếu đa giác là tam giác) Nếu các đỉnh của đa giác không nằm trong cùng một mặt phẳng, thì sau nhiều phép quay khác nhau trong không gian , các thay đổi trong điểm nhìn và phép chiếu lên màn hình hiển thị, các điểm có thể không còn tạo thành một đa giác lồi đơn giản nữa Ví dụ, hãy tưởng tượng một hình tứ giác bốn điểm trong đó các điểm nằm ngoài mặt phẳng một chút và nhìn nó gần như cạnh Bạn có thể nhận được một đa giác không đơn giản giống như một chiếc nơ, như trong​Hình 2.1.2.3​, không được đảm bảo hiển thị chính xác Tình huống này không phải là bất thường nếu bạn ước lượng các bề mặt cong bằng các

tứ giác tạo bởi các điểm nằm trên bề mặt thật Bạn luôn có thể tránh được vấn đề bằng cách sử dụng hình tam giác, vì ba điểm bất kỳ luôn nằm trên một mặt phẳng

Hình 2.1.2.3:​ Đa giác phi mặt phẳng được biến đổi thành đa giác không đơn giản

● Hình chữ nhật (Rectangles)

Vì hình chữ nhật rất phổ biến trong các ứng dụng đồ họa, OpenGL cung cấp một bản vẽ hình chữ nhật hình học cơ sở, ​glRect * ()​ Bạn có thể vẽ một hình chữ nhật dưới dạng một đa giác, nhưng việc triển khai OpenGL cụ thể của bạn có thể đã tối ưu hóa ​glRect * () cho hình chữ nhật

void ​glRect​{sifd}(TYPE ​x1​, TYPE ​y1​, TYPE ​x2​, TYPE ​y2​);

void ​glRect​{sifd}v(const TYPE ​*v1​, const TYPE ​*v2​);

Lưu ý rằng mặc dù hình chữ nhật bắt đầu với một hướng cụ thể trong không gian ba chiều (trong mặt phẳng xy và song song với các trục), bạn có thể thay đổi điều này bằng cách áp dụng các phép quay hoặc các phép biến đổi khác (Xem Chương 3 để biết thông tin về cách thực hiện việc này)

Trang 13

● Đường cong và bề mặt

Mọi đường hoặc bề mặt cong trơn đều có thể được tính gần đúng — với bất kỳ mức độ chính xác nào tùy ý — bằng các đoạn đường ngắn hoặc các vùng đa giác nhỏ Do đó, chia nhỏ các đường cong và bề mặt một cách vừa đủ và sau đó xấp xỉ chúng bằng các đoạn thẳng hoặc đa giác phẳng làm cho chúng có vẻ cong (xem Hình 2-5) Nếu bạn nghi ngờ rằng điều này thực

sự hiệu quả, hãy tưởng tượng chia nhỏ cho đến khi mỗi đoạn thẳng hoặc đa giác rất nhỏ đến mức nhỏ hơn một pixel trên màn hình

void ​glVertex​[234]{sifd}(TYPE ​coords​);

void ​glVertex​[234]{sifd}v(const TYPE* ​coords​);

Chỉ định một đỉnh để sử dụng trong việc mô tả một đối tượng hình học Bạn có thể cung cấp tối

đa bốn tọa độ (x, y, z, w) cho một đỉnh cụ thể hoặc ít nhất là hai (x, y) bằng cách chọn phiên bản thích hợp của lệnh Nếu bạn sử dụng phiên bản không chỉ định rõ ràng z hoặc w, z được hiểu là 0 và

w được hiểu là 1 Các lệnh gọi tới glVertex*()​ chỉ có hiệu lực giữa cặp glBegin()​ và

glEnd()​

Ví dụ: Cách sử dụng glVertex*()

chỉ định, thì tọa độ z được hiểu là 0) ​Tọa độ trong ví dụ thứ hai là (0.0, 0.0, 3.1415926535898) (số

Trang 14

dấu phẩy động chính xác kép) Thứ ba ví dụ đại diện cho đỉnh có tọa độ ba chiều (1.15, 0,5, 1,1)

bằng cách sử dụng mảng đỉnh hoạt động của OpenGL

2.1.2.3 Vẽ các hình học cơ sở trong OpenGL (OpenGL geometric drawing primitives)

Bây giờ bạn đã thấy cách chỉ định các đỉnh, bạn vẫn cần biết cách yêu cầu OpenGL tạo một tập hợp các điểm, một đoạn thẳng hoặc một đa giác từ các đỉnh đó Để thực hiện việc này, bạn đặt dấu ngoặc nhọn từng tập đỉnh giữa một lệnh gọi tới ​glBegin () và một lệnh gọi tới ​glEnd ()​ Đối số được truyền cho ​glBegin () xác định loại hình học hình học cơ sở nào được xây dựng từ các đỉnh

Ví dụ dưới đây sẽ vẽ một đa giác được tô màu dựa vào Hình 2.1.2.5​

Hình 2.1.2.5:​ Vẽ một đa giác hoặc một tập hợp các điểm

Nếu bạn đã sử dụng GL_POINTS thay vì GL_POLYGON, hình học cơ sở sẽ chỉ đơn giản là năm

điểm được thể hiện trong Hình 2.1.2.5​ ​Bảng 2-2 trong phần tóm tắt hàm sau đây cho glBegin()

liệt kê 10 đối số có thể có và các kiểu hình học cơ sở tương ứng

void ​glBegin​(GLenum ​mode​);

Trang 15

Bảng 2.1.2.1: Tên và ý nghĩa các loại đối tượng hình học cơ sở

void ​glEnd​(void);

Đánh dấu phần cuối của danh sách dữ liệu đỉnh

Hình 2.1.2.6 cho thấy các ví dụ về tất cả các hình học cơ sở được liệt kê trong ​Bảng 2.1.2.1​, với

các mô tả về các pixel được vẽ cho mỗi đối tượng Lưu ý rằng ngoài các điểm, một số loại đường và

đa giác được xác định Rõ ràng, bạn có thể tìm thấy nhiều cách để vẽ cùng một hình học cơ sở Phương pháp bạn chọn phụ thuộc vào dữ liệu đỉnh của bạn

GL_LINES cặp đỉnh biểu diễn một đoạn thẳng

GL_LINE_STRIP chuỗi các đoạn thẳng nối nhau

GL_LINE_LOOP giống như trên cộng thêm điểm cuối và điểm đầu được nối

với nhau GL_TRIANGLES bộ ba đỉnh tạo thành các tam giác

GL_TRIANGLE_STRIP linked strip of triangles

GL_TRIANGLE_FAN linked fan of triangles

GL_QUADS bộ bốn đỉnh biểu diễn các đa giác

GL_QUAD_STRIP linked strip of quadrilaterals

GL_POLYGON biên của một đa giác lồi, đơn giản

Trang 16

Hình 2.1.2.6:​ Các kiểu hình học cơ sởKhi bạn đọc các mô tả sau, giả sử rằng n đỉnh (v​ 0​, v​1​, v​2​, , v​n–1​) được mô tả giữa một cặp

glBegin()​ và ​glEnd()​

GL_POINTS Vẽ một điểm tại mỗi đỉnh thứ n

GL_LINES Vẽ một chuỗi đoạn thẳng không kết nối Các đoạn được vẽ

Trang 17

Hạn chế khi sử dụng glBegin()​​và ​glEnd()

giữa v0 và v1, giữa v2 và v3, vv Nếu n​ là lẻ, đoạn thẳng cuối cùng được vẽ giữa vn-3 và vn-2, và vn-1 bị bỏ qua

GL_LINE_STRIP Vẽ một đoạn thẳng từ v0 đến v1, rồi từ v1 đến v2, và vv ,

cuối cùng vẽ một đoạn từ vn-2 đến vn-1 Vì thế, tổng cộng

n​–​1​ đoạn được vẽ Không có gì được vẽ trừ khi n​ lớn hơn 1 Không có ràng buộc nào trên các đỉnh mô tả một line strip (hay một line loop); các đoạn thẳng có thể giao nhau tùy ý

GL_LINE_LOOP Giống GL_LINE_STRIP, ngoại trừ đoạn cuối cùng được vẽ từ

vn-1 đến v0, khép kín một vòng

GL_TRIANGLES Vẽ một chuỗi các tam giác (đa giác ba cạnh) sử dụng các đỉnh

v0, v1, v2, rồi v3, v4, v5, và vv Nếu n​ không phải là bội số của 3 thì một hoặc hai đỉnh cuối bị bỏ qua

GL_TRIANGLE_STRIP Vẽ một chuỗi các tam giác (đa giác ba cạnh) sử dụng các đỉnh

v0, v1, v2, rồi v2, v1, v3 (chú ý thứ tự), rồi v2, v3, v4, và vv Thứ tự là để đảm bảo rằng các hình tam giác đều được vẽ với cùng hướng để strip có thể tạo thành một phần của surface một cách chính xác Bảo đảm hướng là quan trọng đối với một

số hoạt động, chẳng hạn như culling (See “Reversing and

Culling Polygon Faces” ở trang 61) n​ phải ít nhất là 3

GL_TRIANGLE_FAN Giống GL_TRIANGLE_STRIP, ngoại trừ các đỉnh là v0, v1,

v2, rồi v0, v2, v3, rồi v0, v3, v4, và vv (xem Hình 2-7)

GL_QUADS Vẽ một chuỗi tứ giác (đa giác bốn cạnh) sử dụng các đỉnh v0,

v1, v2, v3, rồi v4, v5, v6, v7, và vv Nếu n​ không phải là bội

số của 4 thì một, hai, hoặc 3 đỉnh cuối cùng bị bỏ qua

GL_QUAD_STRIP Vẽ một chuỗi tứ giác (đa giác có bốn cạnh) bắt đầu với v0, v1,

v3, v2, rồi v2, v3, v5, v4, rồi v4, v5, v7, v6, và vv (xem Hình 2-7) ​n​ ít nhất phải là 4 trước khi mọi thứ được vẽ Nếu n​ là lẻ, đỉnh cuối cùng bị bỏ qua

GL_POLYGON Vẽ một đa giác sử dụng các điểm v0, … , vn-1 như các đỉnh n

ít nhất phải là 3, nếu không không có gì được vẽ Thêm vào

đó, đa giác phải được đặc tả lồi và không giao nhau Nếu các đỉnh không thỏa mãn các điều kiện trên Kết quả là không dự đoán được

Trang 18

Thông tin quan trọng nhất về các đỉnh là tọa độ của chúng, được xác định bởi lệnh

glVertex*()​ Bạn cũng có thể cung cấp dữ liệu bổ sung về đỉnh cụ thể cho mỗi đỉnh — màu sắc,

2-3 chứa danh sách đầy đủ các lệnh hợp lệ như vậy

Bảng 2.1.2.2 : Các câu lệnh hợp lệ giữa glBegin()​​và ​glEnd()

glEnableClientState() và ​glVertexPointer()​, khi được gọi giữa ​glBegin() và

glEnd()​, có hành vi không xác định nhưng không nhất thiết tạo ra lỗi (Ngoài ra, các quy trình liên

Dưới đây là một cách để vẽ đường tròn

glEvalCoord*()​, ​glEvalPoint*() tạo tọa độ

glCallList()​, ​glCallLists() thực hiện (các) danh sách hiển thị

Trang 19

Lưu ý: Ví dụ này không phải là cách hiệu quả nhất để vẽ một vòng tròn, đặc biệt nếu bạn định

hoặc sử dụng mảng đỉnh để hiển thị chúng

glVertex*() được phát hành Tại thời điểm ​glVertex*() được gọi, OpenGL sẽ gán cho đỉnh

mặc dù có các lệnh màu bổ sung:

glBegin() và ​glEnd()​, mặc dù trong các ứng dụng thực, tất cả các lệnh gọi trong bất kỳ trường

chương trình bằng cách sử dụng mảng đỉnh

2.1.3 Quản lý trạng thái cơ bản (Basic state management)

Trong phần trước, chúng ta đã được tìm hiểu về các biến trạng thái ​(state variable)​, hệ thống màu RGBA và cách chúng liên kết với môi trường OpenGL chịu trách nhiệm quản lý các trạng thái và các biến trạng thái Một vật thể có thể được tạo ra với các yêu cầu về ánh sáng, kết cấu vật thể, xóa các mặt phẳng bị che khuất, tạo hiệu ứng sương mù, và các trạng thái khác ảnh hưởng đến bề ngoài của vật thể

Mặc định thì hầu hết các trạng thái khi được khởi tạo sẽ không được kích hoạt Các trạng thái này đều mất chi phí nhất định để kích hoạt nó, ví dụ như tạo ra vật thể dưới dạng kết cấu của vật thể

Trang 20

(texture) sẽ làm chậm quá trình tạo ra vật thể tuy nhiên sẽ làm tăng chất lượng hiển thị của vật thể, làm cho vật thể chân thực hơn

Để kích hoạt hoặc vô hiệu hóa các trạng thái, chúng ta dùng hai hàm như sau:

● void ​glEnable​(GLenum ​capability​):​ kích hoạt capability​

● void ​glDisable​(GLenum ​capability​):​ vô hiệu hóa capability​

Có hơn 60 giá trị có thể truyền vào cho ​capability​, có thể kể ra như ​GL_BLEND (quản lý sự pha trộn màu của các giá trị RGBA), ​GL_DEPTH_TEST (quản lý, so sánh độ sâu và cập nhật độ sâu của các vật thể), ​GL_FOG (sương mù), ​GL_STIPPLE (đường hoa văn), ​GL_LIGHTING (quản lý ánh sáng)

Đồng thời, chúng ta có thể kiểm tra xem một trạng thái đã được kích hoạt hay chưa bằng hàm:

● GLboolean ​glIsEnabled​(GLenum ​capability​): trả về ​GL_TRUE hoặc ​GL_FALSE​,

phụ thuộc vào capability​ có đang được kích hoạt hay không

Ở phía trên, chúng ta chỉ đang đề cập đến hay trạng thái: kích hoạt hoặc không kích hoạt Tuy nhiên, OpenGL còn có các biến trạng thái phức tạp hơn Ví dụ như hàm ​glColor3f​() với biến

trạng thái là ​GL_CURRENT_COLOR là tổ hợp của ba giá trị nguyên không âm OpenGL sử dụng năm hàm sau để kiểm tra xem các giá trị cụ thể nào được gán trong một biến trạng thái phức hợp:

● void ​glGetBooleanv​(GLenum ​pname​, GLboolean *​params​)

● void ​glGetIntegerv​(GLenum ​pname​, GLint *​params​)

● void ​glGetFloatv​(GLenum ​pname​, GLfloat *​params​)

● void ​glGetDoublev​(GLenum ​pname​, GLdouble *​params​)

● void ​glGetPointerv​(GLenum ​pname​, GLvoid **​params​)

Tham số ​pname ​là hằng số tượng trưng cho biến trạng thái cần lấy giá trị, ​params là con trỏ trỏ đến vị trí của mảng chứa các giá trị trả về Các hàm trên trả về lần lượt các giá trị kiểu logic, số nguyên, số thực và con trỏ của biến trạng thái Ví dụ như để lấy trạng thái hiện tại của RGBA color, chúng ta có thể gọi hàm ​glGetIntegerv​(​GL_CURRENT_COLOR​, ​params​) hoặc

glGetFloatv​(​GL_CURRENT_COLOR, params​)​

Những câu lệnh trên vẫn chưa là tất cả nhưng cũng đã xử lý được hầu hết các yêu cầu lấy thông tin của các biến trạng thái

2.1.4 Hiển thị điểm, đường thẳng và đa giác (Displaying points, lines and polygons)

Theo mặc định, một điểm được vẽ dưới dạng một pixel trên màn hình, một đường thẳng được vẽ liền mạch và rộng 1 pixel, và các đa giác được vẽ liền mạch Các đoạn sau thảo luận chi tiết về cách thay đổi các chế độ hiển thị mặc định này

2.1.4.1 Hiển thị điểm (Point details)

Để kiểm soát kích thước của một điểm được hiển thị, hãy sử dụng ​glPointSize() và cung cấp kích thước mong muốn tính bằng pixel làm đối số

void ​glPointSize​(GLfloat ​size​);

Đặt chiều rộng tính bằng pixel cho các điểm được kết xuất; kích thước phải lớn hơn 0,0 và theo mặc định là 1,0

Trang 21

Các pixel thực tế trên màn hình được vẽ cho các độ rộng điểm khác nhau phụ thuộc vào việc tính năng khử răng cưa (​anti-aliasing​) có được bật hay không Khử răng cưa (​anti-aliasing​) là một kỹ thuật để làm mịn các điểm và đường thẳng khi chúng được render, xem “Antialiasing” trongChương 6 để biết thêm chi tiết) Nếu khử răng cưa (​anti-aliasing​ ) bị tắt (giá trị mặc định), độ rộng phân số được làm tròn thành độ rộng nguyên và vùng pixel hình vuông được vẽ Do đó, nếu chiều rộng là 1,0, hình vuông là 1 pixel x 1 pixel; nếu chiều rộng là 2.0, hình vuông là 2 pixel x 2 pixel, Với tính năng khử răng cưa hoặc đa lấy mẫu được bật, một nhóm pixel hình tròn sẽ được vẽ và các pixel trên ranh giới thường được vẽ ở cường độ thấp hơn đầy đủ để làm cho cạnh trông mịn hơn Trong chế độ này, chiều rộng không lớn hơn không được làm tròn

Hầu hết các triển khai OpenGL hỗ trợ kích thước điểm rất lớn Bạn có thể truy vấn kích thước tối thiểu và tối đa cho các điểm bí danh bằng cách sử dụng ​GL_ALIASED_POINT_SIZE_RANGE với

glGetFloatv()​ Tương tự như vậy, bạn có thể nhận được phạm vi kích thước được hỗ trợ cho các điểm chống răng cưa bằng cách chuyển ​GL_SMOOTH_POINT_SIZE_RANGE tới ​glGetFloatv() Kích thước của các điểm khử răng cưa được hỗ trợ cách đều nhau giữa kích thước tối thiểu và tối đa cho phạm vi Gọi ​glGetFloatv() với tham số ​GL_SMOOTH_POINT_SIZE_GRANULARITY sẽ trả

về mức độ chính xác của kích thước điểm khử răng cưa đã cho được hỗ trợ Ví dụ: nếu bạn yêu cầu

glPointSize​(2.37) và độ chi tiết được trả về là ​0.1​, thì kích thước điểm được làm tròn thành 2.4​

2.1.4.2 Hiển thị đường thẳng (Line details)

Với OpenGL, bạn có thể xác định dòng với độ rộng khác nhau và dòng được stippled theo những cách khác nhau, tiêu tan, được vẽ bằng dấu chấm xen kẽ và dấu gạch ngang, và vân vân

2.1.4.2.1 Độ rộng đường thẳng

void ​glLineWidth​(GLfloat ​width​);

Đặt chiều rộng, tính bằng pixel, cho các đường được kết xuất; chiều rộng phải lớn hơn 0,0

và theo mặc định là 1.0 Phiên bản 3.1 không hỗ trợ các giá trị lớn hơn 1.0 và sẽ tạo ra lỗi GL_INVALID_VALUE​ nếu giá trị lớn hơn 1.0 được chỉ định

Việc hiển thị thực tế các đường thẳng bị ảnh hưởng bởi chế độ khử răng cưa (​anti-aliasing​ ),tương tự như đối với các điểm (Xem “Antialiasing” trong Chương 6) Nếu không có anti-aliasing, chiều rộng của các đường thẳng 1, 2, và 3 sẽ vẽ các đường thẳng 1, 2 và 3 pixel rộng Với tính năng khử răng cưa (​anti-aliasing​) được bật, độ rộng đường thẳng không phải số nguyên được cho phép

và pixel trên các đường biên thường được vẽ ở mức ít hơn cường độ đầy đủ Giống như với các kích thước điểm, một triển khai OpenGL cụ thể có thể giới hạn độ rộng của các dòng không phân biệt số thành độ rộng dòng chống răng cưa tối đa của nó, được làm tròn đến giá trị số nguyên gần nhất Bạn có thể có được phạm vi độ rộng đường bí danh được hỗ trợ bằng cách sử dụng GL_ALIASED_LINE_WIDTH_RANGE với ​glGetFloatv()​ Để xác định kích thước tối thiểu và tối

đa được hỗ trợ của độ rộng đường chống răng cưa cũng như mức độ chi tiết mà triển khai của bạn

hỗ trợ, hãy gọi ​glGetFloatv()​, với ​GL_SMOOTH_LINE_WIDTH_RANGE vàGL_SMOOTH_LINE_WIDTH_GRANULARITY​ Phải cho phép ngắt dòng bằng cách chuyển

Trang 22

GL_LINE_STIPPLE đến ​glEnable()​; nó bị vô hiệu hóa bằng cách chuyển cùng một đối số tới

glDisable()​

2.1.4.2.2 Đường thẳng hoa văn ( striped lines​)

Để tạo các đường thẳng hoa văn (striped lines - chấm hoặc đứt), bạn sử dụng lệnh

glLineStipple()​ để xác định mẫu stipple , sau đó bạn bật dòng stippling bằng glEnable()​

void ​glLineStipple​(GLint ​factor​, GLushort ​pattern​);

Hình 2.1.4.1:​ Các tham số và đường thẳng tương ứng

Đặt mẫu nhiễu hiện tại cho các đường Đối số mẫu là một chuỗi 16 bit của 0 và 1 và nó được lặp lại khi cần thiết để làm xáo trộn một dòng nhất định Điểm 1 chỉ ra rằng bản vẽ xảy ra và điểm 0 là

nó không xảy ra, trên cơ sở từng pixel, bắt đầu bằng bit bậc thấp của mẫu Mẫu có thể được kéo dài

ra bằng cách sử dụng hệ số , nhân mỗi con của các số 1 và 0 liên tiếp Do đó, nếu ba số 1 liên tiếp xuất hiện trong mẫu, chúng sẽ được kéo dài thành sáu nếu hệ số là 2 Hệ số được kẹp nằm trong khoảng từ 1 đến 256

Phải cho phép ngắt dòng bằng cách chuyển ​GL_LINE_STIPPLE đến ​glEnable()​; nó bị vô

hiệu hóa bằng cách chuyển cùng một đối số tới glDisable()​

2.1.4.3 Hiển thị đa giác (Polygon details)

Đa giác thường được vẽ bằng cách điền vào tất cả các pixel nằm trong ranh giới, nhưng bạn cũng

có thể vẽ chúng dưới dạng đa giác có đường viền hoặc đơn giản là các điểm ở các đỉnh Một đa giác được lấp đầy có thể được tô liền mạch hoặc bị xếp bằng một mẫu nhất định Mặc dù các chi tiết chính xác bị bỏ qua ở đây, nhưng các đa giác đã điền được vẽ theo cách mà nếu các đa giác liền kề

có chung một cạnh hoặc đỉnh, các pixel tạo nên cạnh hoặc đỉnh được vẽ chính xác một lần — chúng chỉ được đưa vào một trong các đa giác Điều này được thực hiện để các đa giác trong suốt một phần không có các cạnh của chúng được vẽ hai lần, điều này sẽ làm cho các cạnh đó có vẻ tối hơn (hoặc sáng hơn, tùy thuộc vào màu bạn đang vẽ) Lưu ý rằng nó có thể dẫn đến đa giác hẹp không

có pixel được lấp đầy trong một hoặc nhiều hàng hoặc cột pixel

2.1.4.3.1 Đa giác dưới dạng điểm, đường viền hoặc tô kín

Trang 23

Một đa giác có hai mặt — trước và sau — và có thể được hiển thị khác nhau tùy thuộc vào mặt nào hướng về phía người xem Điều này cho phép bạn có những cái nhìn cận cảnh về các vật thể rắn trong đó có sự phân biệt rõ ràng giữa các phần bên trong và phần bên ngoài Theo mặc định, cả mặt trước và mặt sau đều được vẽ theo cùng một cách Để thay đổi điều này, hoặc chỉ vẽ đường

viền hoặc đỉnh, hãy sử dụng glPolygonMode()​

Điều khiển chế độ vẽ cho mặt trước và mặt sau của đa giác Tham số mặt có thểGL_FRONT_AND_BACK, GL_FRONT​, hoặc ​GL_BACK​; chế độ có thể là ​GL_POINT​, ​GL_LINE hoặc GL_FILL để cho biết đa giác nên được vẽ dưới dạng điểm, được viền hay được tô Theo mặc định,

cả mặt trước và mặt sau đều được tô

Phiên bản 3.1 chỉ chấp nhận​GL_FRONT_AND_BACK làm giá trị cho khuôn mặt và hiển thị các đa giác theo cùng một cách bất kể chúng quay mặt trước hay quay sau

Ví dụ: bạn có thể điền mặt trước và mặt sau được phác thảo bằng với hai lời gọi hàm sau:

2.1.4.3.2 Đảo ngược và hủy bỏ các mặt đa giác

Theo quy ước, các đa giác có các đỉnh xuất hiện theo thứ tự ngược chiều kim đồng hồ trên màn hình được gọi là mặt trước Bạn có thể xây dựng bề mặt của bất kỳ chất rắn “hợp lý” nào — một nhà toán học sẽ gọi bề mặt như vậy là một đa tạp có thể định hướng (hình cầu, bánh rán và ấm có thể định hướng; chai Klein và dải Mobius thì không) —từ đa giác có định hướng nhất quán Nói cách khác, bạn có thể sử dụng tất cả các đa giác theo chiều kim đồng hồ hoặc tất cả các đa giác ngược chiều kim đồng hồ (Đây thực chất là định nghĩa toán học của định hướng )

Giả sử bạn đã mô tả một cách nhất quán mô hình của một bề mặt có thể định hướng nhưng lại có hướng theo chiều kim đồng hồ ở bên ngoài Bạn có thể hoán đổi những gì OpenGL coi là mặt sau

bằng cách sử dụng hàm glFrontFace()​, cung cấp hướng mong muốn cho các đa giác mặt trước

void ​glFrontFace​(GLenum ​mode​);

Kiểm soát cách xác định mặt trước của đa giác Theo mặc định, chế độ là ​GL_CCW​, tương ứng vớihướng ngược chiều kim đồng hồ của các đỉnh được sắp xếp của đa giác được chiếu ​​trong các tọa độ cửa sổ Nếu chế độ là GL_CW​, các mặt có hướng theo chiều kim đồng hồ được coi là mặt trước Trong một bề mặt được bao bọc hoàn toàn được xây dựng từ các đa giác trong suốt với hướng nhất quán, không có đa giác nào ở mặt sau có thể nhìn thấy được — chúng luôn bị che bởi các đa giác mặt trước Nếu bạn đang ở bên ngoài bề mặt này, bạn có thể bật tính năng chọn lọc để loại bỏ các đa giác mà OpenGL xác định là mặt sau Tương tự, nếu bạn đang ở bên trong đối tượng, chỉ có thể nhìn thấy các đa giác mặt sau Để hướng dẫn OpenGL loại bỏ các đa giác mặt trước hoặc mặt

sau, hãy sử dụng lệnh glCullFace()​ và bật tính năng sắp xếp bằng glEnable()​

void ​glCullFace​(GLenum ​mode​);

Cho biết các đa giác nào cần được loại bỏ (culled) trước khi chúng được chuyển đổi thành tọa độ màn hình Chế độ là ​GL_FRONT, GL_BACK hoặc ​GL_FRONT_AND_BACK để biểu thị mặt trước, mặt sau hoặc tất cả các đa giác Để có hiệu lực, việc culling phải được kích hoạt bằng cách sử dụng

glEnable() với ​GL_CULL_FACE​; nó có thể được vô hiệu hóa với ​glDisable() và cùng một đối

số

glPolygonMode(GL_FRONT, GL_FILL);

glPolygonMode(GL_BACK, GL_LINE);

Trang 24

2.1.4.3.3 Đa giác hoa văn (​Stipping Polygons​)

Theo mặc định, các đa giác đã được vẽ bằng một mẫu đồng nhất Chúng cũng có thể được lấp đầy bằng một mẫu stipple căn chỉnh theo cửa sổ 32 bit x 32 bit , mà bạn chỉ định bằng

glPolygonStipple()​

void ​glPolygonStipple​(const GLubyte ​*mask​);

Định nghĩa hoa văn hiện hành cho các đa giác đã được tô màu Đối số mask là con trỏ đến một bitmap 32 × 32 được hiểu là một mask của 0 và 1 Trong trường hợp 1 xuất hiện, pixel tương ứng trong đa giác được vẽ và trường hợp 0 xuất hiện, sẽ không có gì được vẽ

Ngoài việc xác định mô hình xáo trộn đa giác hiện tại, bạn phải bật chức năng đa giác hoa văn:

Sử dụng glDisable()​ với cùng một đối số để vô hiệu hóa polygon stippling

2.1.5 Vectơ pháp tuyến (Normal vector)

Vectơ pháp tuyến được định nghĩa ở đây là vectơ có hướng vuông góc với bề mặt cho trước Đối với mặt phẳng, hướng của vectơ pháp tuyến là như nhau nhưng đối với mặt cong, hướng của vectơ pháp tuyến có thể khác nhau xét trên từng điểm của mặt cong đó Với OpenGL, chúng ta có thể xác định được vectơ pháp tuyến của từng đa giác hoặc từng đỉnh Các đỉnh thuộc cùng một đa giác có thể có chung vectơ pháp tuyến (đối với mặt phẳng) hoặc khác vectơ pháp tuyến (đối với mặt cong) Vectơ pháp tuyến của vật thể xác định hướng của bề mặt vật thể đó trong không gian Nói cụ thể hơn, hướng của vật thể liên quan đến nguồn ánh sáng - vectơ pháp tuyến quyết định bề mặt nào của vật thể được nhận nhiều hay ít ánh sáng

Chúng ta sử dụng hàm ​glNormal*() để gán giá trị tham số truyền vào cho vectơ pháp tuyến hiện tại, theo sau là hàm ​glVertex*() nhằm xác định đỉnh nào sẽ được gán vectơ pháp tuyến.

Cụ thể hai hàm như sau:

● void ​glNormal3​{bsidf}(TYPE ​nx​, TYPE ​ny​, TYPE ​nz​)

● void ​glNormal3​{bsidf}v(const TYPE *​v​)

Hàm đầu tiên gọi là phiên bản non-vectơ (có chữ v ở cuối tên hàm), giúp gán giá trị tham số truyền vào (cụ thể là ​nx​,​ny​,​nz​) cho vectơ pháp tuyến hiện tại Ngoài ra, hàm thứ hai có chức năng tương tự như hàm đầu tiên, gọi là phiên bản vectơ (không có chữ v ở cuối tên hàm), truyền tham số đầu vào là một mảng gồm ba giá trị tương ứng với giá trị cần gán cho vectơ pháp tuyến Các phiên bản b, s và i giúp tăng hoặc giảm giá trị theo tỷ lệ một cách tuyến tính của các tham số truyền vào, nằm trong khoảng [-1.0, 1.0]​

Trang 25

Lưu ý rằng, xuất phát tại mỗi điểm cho trước nằm trên bề mặt sẽ có hai vectơ vuông góc với bề mặt có hướng ngược nhau Cụ thể hơn, nếu vectơ đầu tiên có dạng là ​(x, y, z) thì vectơ thứ hai

sẽ có dạng ​(-x, -y, -z)​ Đồng thời, chúng ta cũng nhớ rằng vectơ pháp tuyến chỉ có ý nghĩa về hướng, độ lớn của vectơ pháp tuyến là không quan trọng, vì vậy, chúng ta có thể biểu diễn vectơ pháp tuyến với độ dài bất kỳ, sau đó chúng đều được chuyển đổi về độ dài 1 (vectơ đơn vị hay vectơ chuẩn tắc) trước khi những tính toán liên quan đến ánh sáng chiếu đến vật thể được thực hiện

Để chuyển đổi vectơ pháp tuyến thành dạng chuẩn tắc, ta chia lần lượt các tọa độ x, y, z của vectơ cho √x2+ y2+ z2

Vectơ pháp tuyến chỉ tồn tại ở dạng chuyển tắc khi các phép biến đổi hình học chỉ là phép xoay

theo tỷ lệ ​(scale) hay phép kéo (​shear) hoặc đơn giản là nếu chúng ta xác định một vectơ pháp tuyến với độ dài không bằng 1, cần phải gọi hàm ​glEnable​(GL_NORMALIZE) để biến vectơ pháp tuyến thành dạng chuẩn tắc

Nếu chúng ta đã xác định một vectơ pháp tuyến chuẩn tắc và chỉ thực hiện duy nhất phép tăng hoặc giảm theo tỉ lệ ​(scale)​, có thể gọi hàm ​glEnable​(GL_RESCALE_NORMAL) ​để thực hiện phép

biến đổi, sau đó sẽ trả vectơ pháp tuyến về dạng chuẩn tắc trở lại Cách làm này sẽ đỡ tốn chi phí hơn việc thực hiện đầy đủ các bước tính toán của hàm ​glEnable​(GL_NORMALIZE)​ Với trạng thái

mặc định, GL_NORMALIZE​ và ​GL_RESCALE_NORMAL​ đều chưa được kích hoạt

số hệ thống, các lệnh gọi hàm tốn rất nhiều chi phí và có thể cản trở hiệu suất

Một vấn đề khác là việc xử lý dư thừa các đỉnh được chia sẻ giữa các đa giác liền kề Ví dụ, hình lập phương trong Hình 2.1.6.1 có sáu mặt và tám đỉnh dùng chung Thật không may, nếu phương pháp tiêu chuẩn để mô tả đối tượng này được sử dụng, mỗi đỉnh phải được chỉ định ba lần: mỗi lần cho mỗi mặt sử dụng nó Do đó, ta phải xử lý 24 đỉnh mặc dù 8 đỉnh là đủ

Trang 26

OpenGL có mảng lưu trữ các đỉnh cho phép bạn chỉ định rất nhiều dữ liệu liên quan đến đỉnh chỉ với một vài mảng và truy cập dữ liệu đó với một vài lệnh gọi hàm Sử dụng quy trình mảng lưu đỉnh, tất cả 20 đỉnh trong một đa giác 20 cạnh có thể được đưa vào một mảng và được gọi bằng một hàm Nếu mỗi đỉnh cũng có một mặt chuẩn, tất cả 20 mặt chuẩn có thể được đưa vào một mảng khác và cũng được gọi với một hàm

Việc sắp xếp dữ liệu trong các mảng đỉnh có thể làm tăng hiệu suất của ứng dụng Sử dụng mảng các đỉnh làm giảm số lần gọi hàm, giúp cải thiện hiệu suất Ngoài ra, việc sử dụng mảng đỉnh có thể cho phép sử dụng lại các đỉnh đã được xử lý

Lưu ý: Mảng đỉnh đã trở thành tiêu chuẩn trong Phiên bản 1.1 của OpenGL Phiên bản 1.4 đã thêm hỗ trợ để lưu trữ các tọa độ sương mù và màu phụ trong các mảng đỉnh

3 Vẽ hình với dữ liệu OpenGL lấy dữ liệu từ tất cả các mảng đã kích hoạt bằng cách tham chiếu đến các con trỏ Trong mô hình máy khách-máy chủ, dữ liệu được chuyển đến không gian địa chỉ của máy chủ Có ba cách để làm điều này:

○ Truy cập các phần tử mảng riêng lẻ (nhảy xung quanh một cách ngẫu nhiên)

○ Tạo danh sách các phần tử mảng riêng lẻ (nhảy xung quanh một cách có phương pháp)

tế, có thể sẽ kích hoạt tối đa sáu mảng Ví dụ: không chắc rằng sẽ kích hoạt cả ​GL_COLOR_ARRAY

và ​GL_INDEX_ARRAY​, vì chế độ hiển thị của chương trình hỗ trợ chế độ RGBA hoặc chế độ chỉ mục màu, nhưng có thể không đồng thời cả hai

● void ​glEnableClientState​(GLenum ​array​)

Chỉ định mảng để bật Hằng số biểu tượng GL_VERTEX_ARRAY,GL_COLOR_ARRAY,GL_SECONDARY_COLOR_ARRAY,GL_INDEX_ARRAY,GL_NORMAL_ARRAY,GL_FOG_COORD_ARR

Trang 27

AY,GL_TEXTURE_COORD_ARRAY ​và ​GL_EDGE_FLAG_ARRAY là các tham số có thể chấp nhận được

Lưu ý: ​ Phiên bản 3.1 chỉ hỗ trợ dữ liệu mảng đỉnh được lưu trữ trong các đối tượng đệm.

Nếu bạn sử dụng ánh sáng, bạn có thể muốn xác định một bề mặt bình thường cho mọi đỉnh Để

sử dụng mảng đỉnh cho trường hợp đó, bạn kích hoạt cả mảng tọa độ bình thường bề mặt và mảng tọa độ đỉnh:

glEnableClientState​(GL_NORMAL_ARRAY);

glEnableClientState​(GL_VERTEX_ARRAY);

Giả sử rằng bạn muốn tắt ánh sáng tại một số điểm và chỉ vẽ hình học sử dụng một màu duy nhất Bạn muốn gọi ​glDisable​() để tắt trạng thái chiếu sáng Bây giờ ánh sáng đã bị ngừng hoạt động, bạn cũng muốn ngừng thay đổi các giá trị của trạng thái bình thường bề mặt, điều này rất lãng

phí công sức Để làm điều này, gọi glDisableClientState​(GL_NORMAL_ARRAY)​

● void ​glDisableClientState​(GLenum ​array​)

Chỉ định mảng để vô hiệu hóa Nó chấp nhận các hằng biểu tượng giống như

glEnableClientState​()​

Bạn có thể tự hỏi tại sao các kiến trúc sư của OpenGL lại tạo ra những tên lệnh mới (và dài) này, chẳng hạn như ​gl*ClientState()​ Tại sao bạn không thể chỉ gọi ​glEnable​() và glDisable​()​? Một lý do là ​glEnable​() và ​glDisable​() có thể được lưu trữ trong danh sách hiển thị, nhưng đặc điểm kỹ thuật của mảng đỉnh không thể do dữ liệu vẫn ở phía máy khách

Nếu tính năng đa văn bản được bật, việc bật và tắt mảng máy khách chỉ ảnh hưởng đến đơn vị tạo kết cấu đang hoạt động

2.1.6.2 Bước 2: Đưa dữ liệu vào các mảng

Có một cách đơn giản mà bằng cách đó một lệnh chỉ định một mảng duy nhất trong không gian máy khách Có tám quy trình khác nhau để chỉ định mảng — một quy trình cho mỗi loại mảng Ngoài ra còn có một lệnh có thể chỉ định một số mảng không gian máy khách cùng một lúc, tất cả đều bắt nguồn từ một mảng xen kẽ duy nhất

● void ​glVertexPointer​(GLint ​size, ​GLenum ​type, ​GLsizei ​stride,​const

GLvoid *​pointer​);

Chỉ định nơi dữ liệu tọa độ không gian có thể được truy cập pointer là địa chỉ bộ nhớ của tọa độ đầu tiên của đỉnh đầu tiên trong mảng type chỉ định kiểu dữ liệu ​(GL_SHORT, GL_INT,GL_FLOAT hoặc GL_DOUBLE) của mỗi tọa độ trong mảng size là số tọa độ trên mỗi đỉnh, phải là

2, 3 hoặc 4 bước là độ lệch byte giữa các đỉnh liên tiếp Nếu bước là 0, các đỉnh được hiểu là được đóng gói chặt chẽ trong mảng

Để truy cập bảy mảng khác, có bảy quy trình tương tự:

● void ​glColorPointer​(GLint ​size, ​GLenum ​type, ​GLsizei ​stride, ​constGLvoid *​pointer​);

● void ​glSecondaryColorPointer​(GLint ​size, ​GLenum ​type, ​GLsizei

● void ​glIndexPointer​(GLenum ​type, ​GLsizei ​stride,​const GLvoid

*​pointer​);

Trang 28

● void ​glNormalPointer​(GLenum ​type, ​GLsizei ​stride,​const GLvoid

*​pointer​);

● void ​glFogCoordPointer​(GLenum ​type, ​GLsizei ​stride,​const GLvoid

*​pointer​);

● void ​glTexCoordPointer​(GLint ​size, ​GLenum ​type, ​GLsizei

● void ​glEdgeFlagPointer​(GLsizei ​stride,​const GLvoid *​pointer​);

Lưu ý: Các thuộc tính đỉnh bổ sung, được sử dụng bởi trình đổ bóng có thể lập trình, có thể được lưu trữ trong mảng đỉnh Đối với Phiên bản 3.1, chỉ các mảng đỉnh chung được hỗ trợ để lưu trữ dữ liệu đỉnh

Sự khác biệt chính giữa các quy trình là kích thước và kiểu là duy nhất hay phải được chỉ định

Ví dụ, một bề mặt chuẩn luôn có ba thành phần, vì vậy việc xác định số tọa độ của nó là thừa Biến đánh dấu cạnh ​(edge flag​) luôn là một biến logic duy nhất, vì vậy cả số tọa độ và kiểu đều không

cần được đề cập Bảng 2.1.6.1 hiển thị các giá trị pháp lý cho kích thước và kiểu dữ liệu

Đối với các triển khai OpenGL hỗ trợ đa văn bản, việc chỉ định một mảng tọa độ kết cấu bằng

glTexCoordPointer()​ chỉ ảnh hưởng đến đơn vị kết cấu hiện đang hoạt động

Bảng 2.1.6.1: Kích thước mảng đỉnh (Giá trị trên mỗi đỉnh) và kiểu dữ liệu

GL_DOUBLE

GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, GL_DOUBLE

GL_SHORT, GL_UNSIGNED_SHORT, GL_INT, GL_UNSIGNED_INT, GL_FLOAT, GL_DOUBLE

GL_INT, GL_FLOAT, GL_DOUBLE

GL_FLOAT, GL_DOUBLE

GL_DOUBLE

GLboolean​)

Trang 29

Ví dụ dưới sử dụng mảng đỉnh cho cả màu RGBA và tọa độ đỉnh Các giá trị dấu phẩy động RGB và tọa độ số nguyên (x, y) tương ứng của chúng được tải vào ​GL_COLOR_ARRAY vàGL_VERTEX_ARRAY

Bước

Tham số bước cho các quy trình ​gl*Pointer() ​cho OpenGL biết cách truy cập dữ liệu bạn

cung cấp trong mảng con trỏ của bạn Giá trị của nó phải là số byte giữa phần bắt đầu của hai phần

tử con trỏ liên tiếp hoặc bằng không, đây là một trường hợp đặc biệt Ví dụ: giả sử bạn đã lưu trữ cả tọa độ RGB và (x, y, z) của đỉnh trong một mảng, chẳng hạn như sau:

Để chỉ tham chiếu các giá trị màu trong mảng đan xen, lệnh gọi sau bắt đầu từ đầu mảng (cũng

có thể được chuyển dưới dạng ​& đan xen ​[0]​) và nhảy lên trước 6 * ​sizeof​(​GLfloat​) ​byte, là kích thước của cả hai màu và giá trị tọa độ đỉnh Bước nhảy này là đủ để đến đầu dữ liệu cho đỉnh tiếp theo:

glColorPointer(​3, GL_FLOAT, 6*​sizeof​(GLfloat), &intertwined[0]​)​;

Đối với con trỏ tọa độ đỉnh, bạn cần bắt đầu từ xa hơn trong mảng, ở phần tử thứ tư của đan xen (hãy nhớ rằng lập trình viên C bắt đầu đếm ở số 0):

glVertexPointer(​3, GL_FLOAT, 6*​sizeof​(GLfloat), &intertwined[3]​)​;

Nếu dữ liệu của bạn được lưu trữ tương tự như mảng đan xen ở trên, bạn có thể thấy cách tiếp cận được mô tả trong “Mảng xen kẽ” trên phần 2.1.6.6 thuận tiện hơn cho việc lưu trữ dữ liệu của bạn

Với bước bằng 0, mỗi loại mảng đỉnh (màu RGB, chỉ số màu, tọa độ đỉnh, v.v.) phải được đóng gói chặt chẽ Dữ liệu trong mảng phải đồng nhất; nghĩa là, dữ liệu phải là tất cả các giá trị màu RGB, tất cả các tọa độ đỉnh hoặc tất cả một số dữ liệu khác tương tự theo một số cách

2.1.6.3 Bước 3: Vẽ hình với dữ liệu

static​ GLint vertices[] = {25, 25, 100, 325, 175, 25, 175, 325,

glColorPointer(3, GL_FLOAT, 0, colors);

glVertexPointer(2, GL_INT, 0, vertices);

static​ GLfloat intertwined[] = {

Trang 30

Cho đến khi nội dung của mảng đỉnh được bỏ tham chiếu, các mảng vẫn ở phía máy khách và nội dung của chúng dễ dàng thay đổi Trong Bước 3, nội dung của các mảng được thu thập, gửi đến máy chủ và sau đó được gửi xuống đường dẫn xử lý đồ họa để hiển thị

Có thể lấy dữ liệu từ một phần tử mảng duy nhất (vị trí được lập chỉ mục), từ danh sách có thứ tự các phần tử mảng (có thể được giới hạn trong một tập con của toàn bộ dữ liệu mảng đỉnh) hoặc từ một chuỗi các phần tử mảng

Tham chiếu ngược ( Deferencing​) đến một phần tử mảng đơn

● void ​glArrayElement​(GLint ​ith​);

Lấy dữ liệu của một (đỉnh thứ i) cho tất cả các mảng hiện đang được bật Đối với mảng tọa độ đỉnh, lệnh tương ứng sẽ là ​glVertex [size][type] v()​, trong đó size là một trong [2, 3, 4] và type là một trong ​[s, i, f, d] for ​GLshort, GLint, GLfloat ​và GLdouble, tương ứng

Cả kích thước và kiểu đều được xác định bởi ​glVertexPointer()​ Đối với các mảng được bật

Ví dụ: Sử dụng ​glArrayElement()​ để xác định màu sắc và đỉnh

Khi được thực thi, năm dòng mã sau cùng có tác dụng tương tự

glEnableClientState(GL_COLOR_ARRAY);

glEnableClientState(GL_VERTEX_ARRAY);

glColorPointer(3, GL_FLOAT, 0, colors);

glVertexPointer(2, GL_INT, 0, vertices);

Trang 31

​glArrayElement() chỉ là một lệnh gọi hàm duy nhất trên mỗi đỉnh, nó có thể giảm số lượng lệnh gọi hàm, điều này làm tăng hiệu suất tổng thể Hãy cảnh báo rằng nếu nội dung của

mảng được thay đổi giữa glBegin() và ​glEnd()​, không có gì đảm bảo rằng bạn sẽ nhận được

dữ liệu gốc hoặc dữ liệu đã thay đổi cho phần tử bạn yêu cầu Để an toàn, không thay đổi nội dung của bất kỳ phần tử mảng nào có thể được truy cập cho đến khi hoàn thành phần hình học cơ sở

Tham chiếu ngược ( Deferencing​)​ ​danh sách các phần tử mảng

glArrayElement() rất phù hợp để “nhảy xung quanh” các mảng dữ liệu của bạn một cách ngẫu nhiên Các quy trình tương tự, ​glDrawElements()​, ​glMultiDrawElements() ​và glDrawRangeElements()​, rất tốt để nhảy xung quanh các mảng dữ liệu của bạn một cách có trật

nhận bởi glBegin()​; ví dụ: GL_POLYGON​, ​GL_LINE_LOOP​, ​GL_LINES, GL_POINTS​, v.v

Hiệu ứng của ​glDrawElements()​ gần giống như kết quả đoạn lệnh này

glDrawElements() cũng kiểm tra để đảm bảo rằng chế độ, số lượng và loại là hợp lệ Ngoài

ra, không giống như trình tự trước đó, việc thực thi ​glDrawElements() ​để lại một số trạng thái không xác định Sau khi thực thi ​glDrawElements()​, màu RGB hiện tại, màu phụ, chỉ số màu, tọa độ bình thường, tọa độ sương mù, tọa độ kết cấu và cờ cạnh là không xác định nếu mảng tương ứng đã được bật

Với​glDrawElements()​, các đỉnh cho mỗi mặt của khối lập phương có thể được đặt trong một mảng chỉ số Ví dụ dưới cho thấy hai cách sử dụng ​glDrawElements() để kết xuất khối lập phương Hình dưới cho thấy cách đánh số của các đỉnh được sử dụng trong ví dụ dưới

Trang 32

Hình 2.1.6.2: ​Khối lập phương với các đỉnh được đánh số

Ví dụ Sử dụng ​glDrawElements()​ để tham chiếu một số phần tử mảng

Lưu ý:​ Đây là một lỗi khi đóng gói glDrawElements()​ giữa một cặp ​glBegin()​/​glEnd()​

Với một số kiểu hình học cơ sở (chẳng hạn như ​GL_QUADS, GL_TRIANGLES và ​GL_LINES​), bạn có thể thu gọn một số danh sách chỉ số lại với nhau thành một mảng Vì hình học cơ sở GL_QUADS diễn giải mỗi nhóm bốn đỉnh là một đa giác duy nhất, bạn có thể thu gọn tất cả các chỉ

số được sử dụng trong Ví dụ trên thành một mảng duy nhất, như được hiển thị trong ví dụ dưới:

Ví dụ: Hai lệnh gọi glDrawElements()​ ​hiển thị hai dải dòng

static​ GLubyte frontIndices[] = {4, 5, 6, 7};

static​ GLubyte rightIndices[] = {1, 2, 6, 5};

static​ GLubyte bottomIndices[] = {0, 1, 5, 4};

static​ GLubyte backIndices[] = {0, 3, 2, 1};

static​ GLubyte leftIndices[] = {0, 4, 7, 3};

static​ GLubyte topIndices[] = {2, 3, 7, 6};

glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, topIndices);

static​ GLubyte allIndices[] = {4, 5, 6, 7, 1, 2, 6, 5, 0, 1, 5, 4,

0, 3, 2, 1, 0, 4, 7, 3, 2, 3, 7, 6};

glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, allIndices);

static​ GLubyte oneIndices[] = {0, 1, 2, 3, 4, 5, 6};

static​ GLubyte twoIndices[] = {7, 1, 8, 9, 10, 11};

Trang 33

Thường quy ​glMultiDrawElements() được giới thiệu trong OpenGL Phiên bản 1.4 để cho

phép kết hợp các hiệu ứng của một số lệnh gọi glDrawElements()​ vào một lệnh gọi

● void ​glMultiDrawElements​(GLenum ​type, ​GLenum ​mode, ​GLsizei *​count,GLsizei ​primcount, ​const GLvoid *​indices​);

Gọi một chuỗi các lệnh primcount (một số) là ​glDrawElements()​ Chỉ số là một mảng con trỏ đến danh sách các phần tử của mảng count là một mảng có bao nhiêu đỉnh được tìm thấy trong mỗi danh sách phần tử mảng tương ứng mode (kiểu hình học cơ sở) và kiểu (kiểu dữ liệu) giống như

chúng ở trong glDrawElements()​

Ví dụ: Hiệu ứng của glMultiDrawElements()

Các lệnh gọi tới ​glDrawElements() trong ví dụ trên có thể được kết hợp thành một lệnh gọi

glMultiDrawElements()​, như được hiển thị trong ví dụ dưới:

Ví dụ : Sử dụng glMultiDrawElements()

glDrawRangeElements() cũng tốt để nhảy xung quanh các mảng dữ liệu và hiển thị nội dung của chúng ​glDrawRangeElements() cũng đưa ra hạn chế bổ sung của một loạt giá trị pháp lý cho các chỉ số của nó, điều này có thể làm tăng hiệu suất chương trình Để có hiệu suất tối ưu, một

số triển khai OpenGL có thể tìm nạp trước (lấy trước khi hiển thị) một lượng giới hạn dữ liệu mảng

đỉnh glDrawRangeElements()​ cho phép bạn chỉ định phạm vi các đỉnh được tìm nạp trước

● void ​glDrawRangeElements​(GLuint ​start, ​GLuint ​end, ​GLenum ​mode,

GLenum ​type, ​GLsizei ​count, ​const GLvoid *​indices​);

Tạo một chuỗi các hình học cơ sở tương tự, nhưng hạn chế hơn, chuỗi được tạo bởi

glDrawElements()​ Một số tham số của ​glDrawRangeElements() giống như các đối tác trong

glDrawElements()​, bao gồm mode (loại hình học cơ sở), count (số phần tử), type (kiểu dữ liệu)

và chỉ số (vị trí mảng của dữ liệu đỉnh) ​glDrawRangeElements() giới thiệu hai tham số mới: bắt đầu và kết thúc, chỉ định một phạm vi giá trị chấp nhận được cho các chỉ số Để hợp lệ, các giá trị trong chỉ số mảng phải nằm giữa đầu và cuối

glDrawElements(GL_LINE_STRIP, 7, GL_UNSIGNED_BYTE, oneIndices); glDrawElements(GL_LINE_STRIP, 6, GL_UNSIGNED_BYTE, twoIndices);

for​ (i = 0; i < primcount; i++) {

if​ (count[i] > 0)

glDrawElements(mode, count[i], type, indices[i]); }

static​ GLubyte oneIndices[] = {0, 1, 2, 3, 4, 5, 6};

static​ GLubyte twoIndices[] = {7, 1, 8, 9, 10, 11};

static​ GLsizei count[] = {7, 6};

static​ GLvoid * indices[2] = {oneIndices, twoIndices};

glMultiDrawElements(GL_LINE_STRIP, count, GL_UNSIGNED_BYTE,

indices, 2);

Trang 34

Việc các đỉnh trong chỉ số mảng tham chiếu bên ngoài phạm vi [bắt đầu, kết thúc] là một sai lầm Tuy nhiên, việc triển khai OpenGL không bắt buộc phải tìm hoặc báo cáo lỗi này Do đó, các giá trị chỉ mục bất hợp pháp có thể tạo ra hoặc không thể tạo ra một điều kiện lỗi OpenGL và hoàn toàn phụ thuộc vào việc triển khai quyết định phải làm gì

Có thể sử dụng glGetIntegerv()​ với GL_MAX_ELEMENTS_VERTICES​ và

GL_MAX_ELEMENTS_INDICES để tìm ra tương ứng, số đỉnh tối đa được đề xuất cần tìm nạp trước

và số lượng tối đa chỉ số (cho biết số lượng đỉnh được hiển thị) được tham chiếu Nếu end - start + 1 lớn hơn mức tối đa được khuyến nghị của các đỉnh tìm nạp trước hoặc nếu số lượng lớn hơn mức tối đa được đề xuất của chỉ số, thì ​glDrawRangeElements() vẫn sẽ hiển thị chính xác, nhưng hiệu suất có thể bị giảm

Không phải tất cả các đỉnh trong phạm vi [bắt đầu, kết thúc] đều phải được tham chiếu Tuy nhiên, trên một số triển khai, nếu bạn chỉ định một phạm vi được sử dụng thưa thớt, bạn có thể xử

lý nhiều đỉnh không được sử dụng một cách không cần thiết

Với ​glArrayElement()​, ​glDrawElements()​, ​glMultiDrawElements()​ và

glDrawRangeElements()​, có thể là bộ đệm triển khai OpenGL của bạn đã được xử lý gần đây (có nghĩa là các đỉnh được chuyển đổi, thắp sáng), cho phép ứng dụng của bạn “tái sử dụng” chúng bằng cách không gửi chúng xuống đường dẫn chuyển đổi thêm lần nữa Lấy ví dụ, hình lập phương nói trên, có sáu mặt (đa giác) nhưng chỉ có tám đỉnh Mỗi đỉnh được sử dụng bởi chính xác ba mặt Nếu không có ​gl*Elements()​, việc hiển thị tất cả sáu mặt sẽ yêu cầu xử lý 24 đỉnh, mặc dù 16 đỉnh là dư thừa Việc triển khai OpenGL của bạn có thể giảm thiểu sự dư thừa và xử lý ít nhất là tám đỉnh (Việc sử dụng lại các đỉnh có thể bị giới hạn ở tất cả các đỉnh trong một lệnh gọi

glDrawElements() hoặc ​glDrawRangeElements()​, một mảng chỉ mục duy nhất cho glMultiDrawElements() ​hoặc, cho ​glArrayElement()​, trong một cặp glBegin()​/ glEnd()​)

Tham chiếu ngược ( Deferencing​) ​đến một chuỗi các phần tử mảng

Trong khi ​glArrayElement()​, ​glDrawElements() và ​glDrawRangeElements() “nhảy xung quanh” các mảng dữ liệu của bạn, ​glDrawArrays()​ sẽ chạy thẳng qua chúng

● void ​glDrawArrays​(GLenum ​mode, ​GLint ​first, ​GLsizei ​count​);

Xây dựng một chuỗi các hình học cơ sở bằng cách sử dụng các phần tử mảng bắt đầu từ đầu tiên

và kết thúc ở đầu tiên + số - 1 của mỗi mảng đã bật chế độ chỉ định loại hình học cơ sở nào được xây dựng và là một trong những giá trị tương tự được chấp nhận bởi glBegin (); ví dụ: GL_POLYGON​,​ GL_LINE_LOOP​,​ GL_LINES​, ​GL_POINTS​,

Hiệu ứng của glDrawArrays()​ gần giống như chuỗi lệnh này:

Như trường hợp của​glDrawElements()​, ​glDrawArrays() ​cũng thực hiện kiểm tra lỗi trên các giá trị tham số của nó và để lại màu RGB hiện tại, màu phụ, chỉ số màu, tọa độ bình thường, tọa

độ sương mù, tọa độ kết cấu và cờ cạnh có giá trị không xác định nếu mảng tương ứng đã được kích

glBegin (mode);

for​ (i = 0; i < count; i++)

glArrayElement(first + i);

glEnd();

Trang 35

Tương tự như ​glMultiDrawElements()​, quy trình ​glMultiDrawArrays() đã được giới thiệu trong OpenGL Phiên bản 1.4 để kết hợp một số lệnh gọi ​glDrawArrays() thành một lệnh

gọi duy nhất

● void ​glMultiDrawArrays(​GLenum ​mode​, GLint ​*first​, GLsizei ​*count​,

GLsizei ​primcount​)

Gọi một chuỗi lệnh primcount (một số) lệnh​glDrawArrays()​ ​mode chỉ định kiểu hình học cơ

sở với các giá trị giống như được chấp nhận bởi ​glBegin()​ ​first và ​count​chứa danh sách các

vị trí mảng cho biết nơi xử lý từng danh sách các phần tử mảng Do đó, đối với danh sách thứ i của các phần tử mảng, một sơ đồ hình học được xây dựng bắt đầu từ [i] đầu tiên và kết thúc ở đầu tiên [i] + count [i] - 1

Hiệu ứng của glMultiDrawArrays()​ giống như :

for (i = 0; i < primcount; i++) {

if (count[i] > 0)

glDrawArrays(mode, first[i], count[i]);

}

2.1.6.4 Chạy lại đối tượng cơ sở

Khi bạn bắt đầu làm việc với các tập dữ liệu đỉnh lớn hơn, bạn có thể thấy rằng bạn cần thực hiện nhiều lệnh gọi đến các quy trình vẽ OpenGL, thường hiển thị cùng một kiểu hình học cơ sở (chẳng hạn như ​GL_TRIANGLE_STRIP​) mà bạn đã sử dụng trước đó cuộc gọi vẽ Tất nhiên, bạn có thể sử dụng các quy trình ​glMultiDraw*()​, nhưng chúng yêu cầu chi phí duy trì các mảng cho chỉ mục bắt đầu và độ dài của mỗi hình học cơ sở

OpenGL Phiên bản 3.1 đã thêm khả năng khởi động lại các hình học cơ sở trong cùng một lệnh gọi bản vẽ bằng cách chỉ định một giá trị đặc biệt, chỉ mục khởi động lại hình học cơ sở, được xử lý đặc biệt bởi OpenGL Khi chỉ mục khởi động lại hình học cơ sở gặp phải trong lệnh gọi, một hình học cơ sở kết xuất mới cùng loại được bắt đầu với đỉnh theo sau chỉ mục Chỉ mục khởi động lại

ban đầu được chỉ định bởi quy trình ​glPrimitiveRestartIndex()​

● void ​glPrimitiveRestartIndex​(GLint ​index​);

Chỉ định chỉ mục phần tử mảng đỉnh được sử dụng để chỉ ra rằng hình học cơ sở mới nên được bắt đầu trong quá trình kết xuất Khi quá trình xử lý các chỉ số phần tử mảng đỉnh gặp một giá trị khớp với chỉ mục, không có dữ liệu đỉnh nào được xử lý, kết thúc đồ họa hình học cơ sở hiện tại và một dữ liệu mới cùng loại được bắt đầu

Khởi động lại ban đầu được kiểm soát bằng cách gọi ​glEnable() hoặc ​glDisable() và chỉ định GL_PRIMITIVE_RESTART,​ như được minh họa trong ví dụ dưới

Ví dụ: Sử dụng glPrimitiveRestartIndex()​ để kết xuất nhiều dải tam giác:

#define BUFFER_OFFSET(offset) ((GLvoid *) NULL + offset)

#define XStart -0.8

#define XEnd 0.8

#define YStart -0.8

Trang 36

#define YEnd 0.8

#define NumXPoints 11

#define NumYPoints 11

#define NumPoints (NumXPoints * NumYPoints)

#define NumPointsPerStrip (2*NumXPoints)

#define NumStrips (NumYPoints-1)

vertices = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);

if​ (vertices == ​NULL​) {

fprintf​(​stderr​, ​"Unable to map vertex buffer\n"​);

exit​(EXIT_FAILURE);

} else​ {

int​ i, j;

GLfloat dx = (XEnd - XStart) / (NumXPoints - 1);

GLfloat dy = (YEnd - YStart) / (NumYPoints - 1);

GLfloat *tmp = vertices;

int​ n = 0;

for​ (j = 0; j < NumYPoints; ++j) {

GLfloat y = YStart + j*dy;

for​ (i = 0; i < NumXPoints; ++i) {

GLfloat x = XStart + i*dx;

*tmp++ = x; *tmp++ = y;

} }

glUnmapBuffer(GL_ARRAY_BUFFER);

glVertexPointer(2,GL_FLOAT,0,BUFFER_OFFSET(0));

Trang 37

/* We allocate an extra restart index because it simplifies

** the element-array loop logic */

glBufferData(GL_ELEMENT_ARRAY_BUFFER, NumStrips*(NumPointsPerStrip+1)*​sizeof​(GLushort), ​NULL​,

GL_STATIC_DRAW);

indices = glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY);

if​ (indices == ​NULL​) {

fprintf​(​stderr​, ​"Unable to map index buffer\n"​);

GLushort bottomRow = j*NumYPoints;

GLushort topRow = bottomRow + NumYPoints;

for​ (i = 0; i < NumXPoints; ++i) {

*index++ = topRow + i; *index++ = bottomRow + i;

}

*index++ = RestartIndex;

} glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);

} glPrimitiveRestartIndex(RestartIndex);

Trang 38

2.1.6.5 Vẽ đối tượng

Nâng cao

OpenGL Phiên bản 3.1 (cụ thể là phiên bản GLSL 1.40) đã bổ sung hỗ trợ cho bản vẽ nâng cao, cung cấp một giá trị bổ sung — gl_InstanceID, được gọi là thực thể ID và chỉ có thể truy cập trong trình đổ bóng đỉnh — được tăng đơn điệu cho mỗi nhóm hình học cơ sở được chỉ định

glDrawArraysInstanced() hoạt động tương tự như ​glMultiDrawArrays()​, ngoại trừ chỉ

số bắt đầu và số lượng đỉnh (như được chỉ định bởi số lượng đầu tiên và số lượng, tương ứng) là

giống nhau cho mỗi lệnh gọi tới glDrawArrays()​

● void ​glDrawArraysInstanced​(GLenum ​mode, ​GLint ​first, ​GLsizei ​count,

GLsizei ​primcount​);

Gọi một cách hiệu quả ​glDrawArrays() thời gian sơ khai, đặt giá trị tô bóng đỉnh GLSL gl_InstanceID trước mỗi lần gọi chế độ chỉ định kiểu hình học cơ sở first và count xác định

phạm vi của các phần tử mảng được chuyển đến glDrawArrays()​

glDrawArraysInstanced() có tác dụng tương tự như chuỗi gọi này (ngoại trừ ứng dụng của bạn không thể cập nhật thủ công gl_InstanceID​):

Tương tự như vậy, ​glDrawElementsInstanced() thực hiện cùng một hoạt động, nhưng cho

phép truy cập ngẫu nhiên vào dữ liệu trong mảng đỉnh

● void ​glDrawElementsInstanced​(GLenum ​mode, ​GLenum ​type, ​const void

*indices​, ​GLsizei ​count, ​GLsizei ​primcount​);

Hiệu quả gọi ​glDrawElements() thời gian sơ khai, thiết lập GLSL giá trị vertex shader

gl_InstanceID trước mỗi lần gọi chế độ chỉ định kiểu hình học cơ sở kiểu chỉ ra kiểu dữ liệu của các chỉ số mảng và phải là một trong những kiểu sau: ​GL_UNSIGNED_BYTE​,GL_UNSIGNED_SHORT hoặc ​GL_UNSIGNED_INT​ các chỉ báo và số đếm xác định phạm vi của các

phần tử mảng được chuyển đến glDrawElements()​

Việc triển khai glDrawElementsInstanced()​ được hiển thị ở đây:

Trang 39

glInterleavedArrays()​ Do đó, để xác định nội dung của mảng đan xen vào mảng màu và đỉnh RGB và cho phép cả hai mảng, gọi lệnh:

glInterleavedArrays(​GL_C3F_V3F, 0, intertwined​)​;

Lệnh gọi tới ​glInterleavedArrays() này cho phép ​GL_COLOR_ARRAY vàGL_VERTEX_ARRAY​ Nó vô hiệu hóa ​GL_SECONDARY_COLOR_ARRAY​, ​GL_INDEX_ARRAY​,GL_NORMAL_ARRAY​, GL_FOG_COORD_ARRAY​, ​GL_TEXTURE_COORD_ARRAY vàGL_EDGE_FLAG_ARRAY​

Lệnh gọi này cũng có tác dụng tương tự như gọi ​glColorPointer() và glVertexPointer()​ để chỉ định các giá trị cho sáu đỉnh trong mỗi mảng Hiện nay

bạn đã sẵn sàng cho Bước 3: gọi glArrayElement()​,​ ​glDrawElements()​,

glDrawRangeElements()​ ​hoặc ​glDrawArrays()​ đến mảng tham chiếu các thành phần mảng Lưu ý rằng ​glInterleavedArrays()​ ​không hỗ trợ cờ cạnh

Cơ chế của ​glInterleavedArrays() ​rất phức tạp và cần tham khảo đến ví dụ dưới và bảng dưới Trong ví dụ và bảng đó, bạn sẽ thấy et, ec, và vi, là các giá trị Boolean cho kết cấu được bật hoặc tắt phối hợp, tô màu và các mảng bình thường; và bạn sẽ thấy st, sc và sv, đó là

kích thước (số lượng thành phần) cho tọa độ kết cấu, màu sắc và mảng đỉnh tc là kiểu dữ liệu cho màu RGBA, là mảng duy nhất có thể có các giá trị xen kẽ không phân điểm pc, pn và pv là các bước được tính toán để chuyển sang các giá trị màu, bình thường và đỉnh riêng lẻ; và s là sải chân (nếu người dùng không chỉ định) để nhảy từ một mảng phần tử tiếp theo

● void ​glInterleavedArrays​(GLenum ​format, ​const void *​pointer,GLsizei ​stride​);

}

gl_InstanceID = 0;

static​ GLfloat intertwined[] =

{1.0, 0.2, 1.0, 100.0, 100.0, 0.0, 1,0, 0,2, 0,2, 0,0, 200,0, 0,0, 1,0, 1,0, 0,2, 100,0, 300,0, 0,0, 0,2, 1,0, 0,2, 200,0, 300,0, 0,0, 0,2, 1,0, 1,0, 300,0, 200,0, 0,0, 0,2, 0,2, 1,0, 200,0, 100,0, 0,0};

Trang 40

Khởi tạo tất cả tám mảng, vô hiệu hóa các mảng không được chỉ định trong định dạng và bật các mảng được chỉ định định dạng là một trong 14 hằng biểu tượng, đại diện cho 14 cấu hình dữ liệu; Bảng dưới hiển thị các giá trị định dạng bước chỉ định độ lệch byte giữa các đỉnh liên tiếp Nếu bước là 0, các đỉnh được hiểu là được đóng gói chặt chẽ trong mảng con trỏ là địa chỉ bộ nhớ của tọa độ đầu tiên của đỉnh đầu tiên trong mảng

Nếu bật tính năng đa văn bản, ​glInterleavedArrays() chỉ ảnh hưởng đến đơn vị kết cấu đang hoạt động Tác dụng của ​glInterleavedArrays() ​giống như việc gọi chuỗi lệnh

trong ví dụ dưới với nhiều giá trị được xác định trong bảng dưới Tất cả số học con trỏ được thực hiện theo đơn vị sizeof (GLubyte)

Ví dụ: ​Hiệu ứng của ​glInterleavedArrays ​(định dạng ​format​, bước đi ​stride​, con trỏ

int​ str;

/* set et, ec, en, st, sc, sv, tc, pc, pn, pv, and s

* as a function of Table 2-5 and the value of format

Ngày đăng: 07/02/2022, 14:29

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w