The Book of Shaders by Patricio Gonzalez Vivo & Jen Lowe

Bahasa Indonesia - Tiếng Việt - 日本語 - 中文版 - 한국어 - Español - Portugues - Français - Italiano - Deutsch - Русский - Polski - English


Mẫu hoa văn

Vì các chương trình shader được tính toán cho từng pixel nên dù bạn có vẽ một hình bao nhiêu lần thì cũng không ảnh hưởng tới số phép tính. Điều đó đặc biệt thích hợp nếu ta dùng shader để vẽ các mẫu hoa văn hoạ tiết lặp lại.

Nina Warmerdam - The IMPRINT Project (2013)

Ở chương này ta sẽ dùng hết tất cả những gì đã học để vẽ rồi lặp lại hình vẽ đó ra toàn bộ canvas. Cũng giống như các chương trước, ta sẽ vẽ hình trong một khu vực khoảng [0.0, 1.0] trên hệ toạ độ, rồi tạo bản sao của các khu vực đó để tạo nên một lưới các hình vẽ lặp lại.

"Hệ thống lưới tạo điều kiện để loài người khám phá và phát minh. Trong thiên nhiên hoang dã, các hoa văn giúp ta nhận biết dễ hơn. Người xưa đã dùng các hoa văn từ rất sớm để trang trí, từ các hoạ tiết trên đồ gốm tới các hoạ tiết khảm hình học trong các nhà tắm La Mã." 10 PRINT, Mit Press, (2013)

Trước hết tôi sẽ nhắc lại hàm fract(). Nó trả về phần thập phân của số thực, tương đương với phép tính phần dư khi chia cho 1 (mod(x,1.0)). Nói cách khác, hàm fract() trả về phần lẻ sau dấu thập phân. Hệ toạ độ của ta thì đã nằm trong khoảng [0.0, 1.0] rồi nên dùng hàm fract() trong khoảng này chẳng có ý nghĩa gì:

void main(){
    vec2 st = gl_FragCoord.xy/u_resolution;
    vec3 color = vec3(0.0);
    st = fract(st);
    color = vec3(st,0.0);
    gl_FragColor = vec4(color,1.0);
}

Nhưng nếu ta tăng kích thước của hệ toạ độ lên - gấp 3 lần chẳng hạn - thì cũng với đoạn code trên, ta sẽ thu được 3 đoạn biến thiên tuyến tính giống nhau: đoạn đầu tiên nằm trong khoảng 0-1, đoạn thứ hai nằm trong khoảng 1-2 và đoạn cuối nằm trong khoảng 2-3.

Vậy là ta đã có 1 lưới 3x3 rồi, hãy coi mỗi ô là một không gian thu nhỏ. Bạn hãy uncomment dòng 27 để vẽ một hình gì đó ở mỗi không gian thu nhỏ nhé. (Do ta đã giãn cả 2 trục x và y với cùng 1 tỉ lệ nên các hình vẽ sẽ không bị bóp méo gì cả). Chú ý: Đoạn code không hề dùng vòng lặp.

Hãy thử sửa code như sau để hiểu rõ hơn:

Apply matrices inside patterns

Vì mỗi ô là một hệ toạ độ thu nhỏ, ta có thể áp dụng các phép biến đổi ma trận để di chuyển, quay và thu phóng từng ô.

Hoạ tiết kẻ ca-rô Tartan kiểu Scotland tạo bởi Kavalenkava

Hoạ tiết so le

Giả sử ta muốn tạo bên một bức tường gạch. Hãy nhìn ảnh dưới đây và bạn sẽ thấy các hàng gạch được xếp so le với nhau. Làm thế nào ta có thể vẽ được hình đó ?

Đầu tiên ta cần xác định được tính chẵn lẻ của mỗi hàng, thì mới biết có cần dịch chuyển hàng đó để tạo nên hoạ tiết so le không.

Để biết hàng đang vẽ là chẵn hay lẻ, ta sẽ dùng hàm tính phần dư (mod()) khi chia cho 2.0. Nếu là phép chia hết thì đó là số chẵn, ngược lại là số lẻ. Hãy uncomment 2 dòng cuối của đoạn code dưới đây.

Bạn có thể thấy tôi đã dùng toán tử ba ngôi (ternary) để kiểm tra xem hàm mod() khi chia cho 2.0 thì phần dư có nhỏ hơn 1.0 không. Dòng cuối cùng, thì tôi chọn cách khác mà vẫn cho ra kết quả tương tự, đó là dùng hàm step(), cách này nhanh hơn. Tại sao lại thế ? Thực ra thì khó mà biết được card đồ hoạ biên dịch và tối ưu code ra sao, nhưng nhiều khả năng là các hàm có sẵn sẽ nhanh hơn các hàm ta tự viết. Còn trong trường hợp này thì step() nhanh hơn vì nó được tối ưu bằng phần cứng, còn các lệnh điều kiện thì rất chậm trên các card đồ hoạ. Vì thế nếu có thể hãy cố gắng dùng các hàm có sẵn và hạn chế dùng các lệnh điều kiện.

Giờ thì ta có công thức tìm ra các hàng lẻ rồi nên chỉ cần dịch chuyển các viên gạch một nửa đơn vị theo trục x trên những hàng này thì sẽ tạo nên hoạ tiết so le thôi. Dòng 14 trong đoạn code dưới đây thực hiện đúng như vậy. Tuy nhìn qua thì thấy ta cũng áp dụng dịch chuyển cho cả các hàng chẵn nữa, nhưng vì kết quả phép tính phần dư bằng 0 cho các hàng này nên thành ra các viên gạch không bị dịch chuyển chút nào và vẫn giữ nguyên vị trí như ta mong muốn.

Hãy thử uncomment dòng 32 nhé, đó là dòng code tôi dùng để kéo giãn 2 trục theo tỉ lệ khác nhau để tạo nên hình viên gạch. Uncomment tiếp dòng 40, bạn sẽ thấy hệ trục toạ độ của mỗi ô lưới được minh hoạ bằng màu đỏ và xanh lá.

Hoạ tiết Truchet

Ta có thể nhận biết từng ô ở hàng chẵn hay lẻ, ở cột chẵn hay lẻ, nên có thể tuỳ ý xử lý hình vẽ mỗi ô dựa vào tính chẵn lẻ này. Hoạ tiết Truchet chỉ cần 1 hình vẽ nhưng chỉ cần quay 90 độ là có thêm phiên bản nữa.

Bằng cách quay từng ô, ta có thể tạo nên vô số thiết kế khác nhau.

Hãy chú ý vào hàm rotateTilePattern() dưới đây, nó chia mỗi ô thành 4 ô nhỏ hơn để chứa cả 4 phiên bản của hoạ tiết Truchet.

Tự thiết kế

Tạo nên các hoạ tiết lặp lại là một bài tập tốt cho trí não khi phải tìm cách tái sử dụng một số lượng rất nhỏ các hoạ tiết ban đầu. Con người đã dùng kỹ thuật này từ rất lâu rồi, từ việc trang trí hoạ tiết trên những tấm vải, cho tới các sàn gạch được lát tỉ mỉ; từ các đường viền Hy Lạp cổ tới các thiết kế cửa Trung Quốc, sự hoà quyện của các hoạ tiết dù lặp lại mà vẫn đa dạng kích thích trí tưởng tượng của con người. Hãy dành chút thời gian để ngắm nhìn các hoạ tiết trang trí mà các hoạ sỹ đã sử dụng để che giấu phần lặp lại và làm ta ngạc nhiên với những sự sắp đặt đầy bất ngờ. Từ các hoạ tiết hình học Ả Rập, tới các hoa văn trên vải của Châu Phi, có cả một vũ trụ các hoa văn hoạ tiết mà ta có thể khám phá và học hỏi.

Quyển 'A handbook of ornament' của Franz Sales Meyer (1920)

Đây cũng là chương cuối của phần Các thuật toán vẽ hình. Ở các chương tiếp theo ta sẽ học cách đưa một ít xáo trộn từ entropy vào shader để tạo nên những hình ảnh độc nhất, không thể đoán trước.