Các thuật toán hình học
Các hàm số cơ bản (Hàm hình dạng - Shape function)
Chương này có thể đặt tên là "bài học sơn hàng rào của ngài Miyagi". Trước đó, ta đã ánh xạ toạ độ x và y sang các kênh RED và GREEN. Ta cũng đã tạo ra một hàm nhận một vector 2 chiều (x, y) làm tham số đầu vào và trả ra một vector 4 chiều (r, g, b, a). Nhưng trước khi đi sâu vào việc biến đổi dữ liệu giữa các kiểu thì ta cần bắt đầu từ những thứ đơn giản hơn... rất nhiều. Đó là việc tạo ra các hàm chỉ xử lý vector 1 chiều. Càng bỏ thời gian và công sức học cho thuần thục kỹ năng này, võ karate-shader của bạn sẽ càng cao siêu.
Cấu trúc code dưới đây sẽ là hàng rào của chúng ta. Trong đó, ta chuẩn hoá giá trị x (st.x
) bằng 2 cách: một là với cường độ sáng (quan sát gradient từ đen sang trắng) và hai là vẽ đồ thị một đường màu xanh lá đè lên trên (trường hợp này giá trị của x được gán trực tiếp cho y). Đừng tập trung quá nhiều vào hàm vẽ đồ thị, ta sẽ đi vào chi tiết sau.
Chú thích: Hàm khởi tạo vec3
sẽ tự động "nhận ra" bạn muốn gán màu của cả 3 kênh RGB giống nhau ở dòng 19. Còn hàm khởi tạo vec4
thì "nhận ra" bạn muốn tạo một vector 4 chiều từ một vector 3 chiều kết hợp một số thực nữa dành cho chiều thứ tư, ở dòng 25.
Đoạn code này là hàng rào của bạn; nên hãy tập trung quan sát kỹ. Bạn sẽ còn sử dụng khoảng 0.0
và 1.0
này nhiều lần nữa. Bạn sẽ thuần thục kỹ năng tạo nên đồ thị này.
Ánh xạ một-một giữa x và y (phần gradient đen trắng) được gọi là nội suy tuyến tính (linear interpolation). Ta có thể thay các hàm toán học khác để thay đổi hình dáng của đồ thị. Ví dụ ta có thể vẽ đồ thị luỹ thừa 5 của x để có được đường cong như hình dưới.
Thú vị phải không ? Ở dòng 22, hãy thử các số mũ khác nhau như: 20.0, 2.0, 1.0, 0.0, 0.2 và 0.02 chẳng hạn. Hiểu được mối quan hệ giữa giá trị và số mũ sẽ rất hữu ích. Bằng cách sử dụng các hàm toán học ở đây bạn sẽ nắm rõ hơn về cách điều khiển các đường cong.
pow()
là hàm có sẵn trong số rất nhiều hàm của GLSL. Hầu hết đều được tăng tốc tính toán bởi phần cứng, có nghĩa là nếu được dùng đúng cách nó sẽ giúp code chạy nhanh hơn.
Hãy thay hàm luỹ thừa ở dòng 22 bằng các hàm khác như: exp()
, log()
và sqrt()
. Một số hàm sẽ cho kết quả thú vị nếu bạn sử dụng hằng số PI. Ở dòng 8 tôi có định nghĩa macro sẽ thay thế tất cả những chỗ PI
xuất hiện bằng hằng số 3.14159265359
.
Step và Smoothstep
GLSL cũng có vài hàm số riêng biệt được tăng tốc bởi phần cứng.
Hàm step()
nhận 2 tham số đầu vào. Tham số đầu tiên là một ngưỡng giới hạn nào đó, còn tham số thứ 2 là giá trị mà ta muốn biết có vượt qua ngưỡng giới hạn kia không. Bất kỳ giá trị nào nhỏ hơn ngưỡng sẽ cho kết quả 0.0
và ngược lại, tất cả các giá trị lớn hơn ngưỡng sẽ cho kết quả 1.0
.
Hãy thử thay giá trị ở dòng 20 của đoạn code dưới đây.
Một hàm tương tự là smoothstep()
. Tham số đầu vào là 1 khoảng min-max kèm thêm 1 giá trị. Hàm này sẽ nội suy giá trị đó trong khoảng min-max, các giá trị nằm ngoài khoảng này sẽ trở min hoặc max tuỳ theo nó nằm ở phía nào của khoảng đã cho.
Ở ví dụ trước, dòng 12, chú ý rằng ta đã dùng hàm smoothstep
để vẽ đồ thị trong hàm plot()
. Nếu muốn đồ thị trồi lên ở một đoạn nào đó thì làm thế nào ? Bằng cách ghép hai hàm smoothstep()
lại. Hãy thay dòng code dưới đây vào dòng 20. Trông như ta đã chẻ đôi canvas ra phải không ?
float y = smoothstep(0.2,0.5,st.x) - smoothstep(0.5,0.8,st.x);
Sin và Cos
Khi bạn muốn dùng Toán để tạo chuyển động, tạo hình hay pha trộn các giá trị, không có gì tốt hơn việc làm quen với sin và cos.
Hai hàm lượng giác cơ bản này kết hợp với nhau để tạo nên những vòng tròn đa năng như dao gấp quân đội Thuỵ Sỹ của MacGyver vậy. Việc tìm hiểu cách chúng hoạt động và kết hợp với nhau ra sao rất quan trọng. Về cơ bản, cho một góc bất kỳ (đơn vị radian), hai hàm này sẽ cho kết quả là tọa độ x (cos) và y (sin) của 1 điểm trên đường tròn có bán kính bằng 1. Và chính việc kết quả thu được từ 2 hàm này vừa biến thiên một cách mềm mại lại còn luôn được chuẩn hoá sẵn theo cặp và cả đơn lẻ (trong khoảng -1 tới 1) khiến cho 2 hàm này trở thành các công cụ siêu hữu ích.
Mặc dù rất khó để mô tả mối liên hệ giữa các hàm lượng giác với đường tròn, nhưng chuyển động đẹp tuyệt trên đây đã làm rất tốt nhiệm vụ mô tả tóm tắt mối liên hệ này.
Hãy nhìn thật kỹ đồ thị hình sine. Chú ý cách mà các giá trị y biến thiên rất mượt giữa +1 và -1. Như ta đã thấy ở ví dụ có sử dụng thời gian ở chương trước, bạn có thể sử dụng tính chất tuần hoàn này của hàm sin()
để áp dụng cho các thuộc tính. Nếu bạn đang đọc trên trình duyệt, bạn có thể sửa đoạn code phía trên để xem các sóng đồ thị thay đổi như thế nào. (Chú ý: Đừng quên dấu chấm phẩy ở cuối dòng.)
Hãy thử các thay đổi sau và xem điều gì xảy ra:
-
Cộng
u_time
với x trước khi gọi hàmsin
. Bạn sẽ thấy sóng đồ thị sẽ dịch chuyển dọc theo trục hoành. -
Nhân x với
PI
trước khi gọi hàmsin
. Bạn sẽ thấy 2 chu kỳ bị co lại và lặp lại mỗi 2 đơn vị số nguyên. -
Nhân
u_time
với x trước khi gọi hàmsin
. Bạn sẽ thấy tần số (frequency) giữa các chu kỳ ngày càng ngắn lại. Chú ý rằng u_time càng lớn thì càng khó nhìn rõ đồ thị do các chu kỳ bị co lại rất nhiều. -
Cộng 1.0 vào
sin(x)
. Và bạn sẽ thấy toàn bộ sóng được nâng lên (displaced) khiến cho giá trị nằm trong khoảng 0.0 và 2.0. -
Nhân
sin(x)
với 2.0. Và bạn sẽ thấy biên độ (amplitude) rộng gấp đôi. -
Tính giá trị tuyệt đối (
abs()
) của hàmsin(x)
. Trông đồ thị sẽ giống như đường đi của quả bóng nảy trên mặt đất. -
Tách riêng phần thập phân bằng hàm
fract()
từ kết quả củasin(x)
. - Làm tròn lên bằng hàm
ceil()
và làm tròn xuống bằng hàmfloor()
từ kết quả củasin(x)
để có được sóng điện tử của các giá trị 1 và -1.
Các hàm hữu ích khác
Chúng tôi vừa mới giới thiệu cho các bạn 1 vài hàm mới. Giờ là lúc thử nghiệm từng hàm một bằng cách uncomment từng dòng dưới đây một. Hãy làm quen với các hàm này. Tôi biết bạn đang thắc mắc... tại sao ? Google nhanh với từ khoá "generative art" sẽ cho bạn câu trả lời. Hãy nhớ rằng các hàm này là hàng rào của chúng ta. Chúng ta đang dần thuần thục với các chuyển động 1 chiều, chỉ có lên và xuống. Sớm thôi, ta sẽ đụng tới các chiều thứ hai, ba và bốn!
Các hàm nâng cao
Golan Levin có tài liệu mô tả rất chi tiết về các hàm phức tạp khác vô cùng hữu ích. Ứng dụng chúng vào GLSL sẽ là một bước đi thông minh để bắt đầu dựng nên thư viện code của chính bạn.
-
Các hàm đa thức: www.flong.com/archive/texts/code/shapers_poly
-
Các hàm luỹ thừa: www.flong.com/archive/texts/code/shapers_exp
-
Các hàm mô phỏng đường tròn và elip: www.flong.com/archive/texts/code/shapers_circ
- Đường cong Bezier và các hàm tương tự: www.flong.com/archive/texts/code/shapers_bez
Như một đầu bếp đi thu thập các kỳ hoa dị thảo, nghệ sỹ kỹ thuật số và các lập trình viên đồ hoạ cũng sẽ có niềm yêu thích riêng với các hàm nội suy của riêng họ.
Iñigo Quiles có 1 bộ sưu tầm các hàm rất hữu ích. Sau khi đọc bài báo này hãy xem cách thực thi các hàm đó trong GLSL. Hãy chú ý tới các tiểu tiết như thêm dấu chấm "." vào sau các số thực và sử dụng cách đặt tên hàm của ngôn ngữ C vào GLSL; ví dụ thay vì powf()
hãy dùng pow()
:
Để tạo động lực cho bạn, đây là 1 ví dụ hoàn hảo (tạo nên bởi Danguafer) về việc thuần thục môn võ karate-hàm-số.
Ở các chương tiếp theo (Next >>) chúng ta sẽ học các chiêu mới. Đầu tiên là trộn màu rồi sau đó là vẽ hình.
Bài tập
Hãy nhìn vào bảng các phương trình dưới đây, được tạo bởi Kynd. Hãy xem cách mà anh ấy kết hợp các hàm lại với nhau để kiểm soát các giá trị nằm trong khoảng 0.0 và 1.0. Giờ là lúc bạn tập luyện bằng cách dựng lại các hàm này. Hãy nhớ rằng càng luyện tập chăm chỉ bạn sẽ càng giỏi võ.
Một vài công cụ cho bạn
Đây là một vài công cụ sẽ giúp bạn vẽ đồ thị các hàm 1 cách trực quan nhất.
- GraphToy: Thêm một sản phẩm nữa của Iñigo Quilez để minh hoạ các hàm GLSL trên WebGL.
- LYGIA Shader Library a shader library of reusable functions that can be include easily on your projects. It's very granular, designed for reusability, performance and flexibility. And can be easily be added to any projects and frameworks.