Môi trường
Ở đây mình sử dụng Unity 2022.3.2f1
, Rider 2023.1
Với Unity bạn có thể cài đặt tùy chọn Script Changes While Playing
là Recompile And Continue Playing
nó sẽ giúp cải thiện việc debug shader với việc phản ánh sự thay đổi của
shader runtime trong PlayMode
Còn khi không trong chế độ PlayMode
bạn có thể thấy mọi thay đổi của shader với việc bật Always Refresh
Shader là gì?
Shader là một đoạn code hướng dẫn chạy trên GPU.
Quy trình dựng hình cơ bản
-
Các bước để vẽ một vật thể lên màn hình
- Bước 1: thu thập dữ liệu về các đỉnh (một vật thể được tạo ra bởi một tập hợp các đỉnh, ngay cả các ảnh phẳng 2D cũng được vẽ từ các tứ giác)
- Bước 2: xử lý các đỉnh từ bước 1, các đỉnh có thể đứng yên hay di chuyển tự do, sau bước này các đỉnh không thể di chuyển được nữa. Vì vậy sau bước này ta sẽ có một danh sách các tam giác được xác định bởi mỗi 3 đỉnh. ở các bước tiếp theo ta sẽ sử lý từng tam giác này một
- Bước 3: từ góc nhìn của camera mỗi tam giác sẽ chiếm một vùng nào đó trên màn hình. Các tam giác được chia nhỏ thành các ô vuông pixel gọi là Fragment. Nếu tam giác ở gần thì có thể cần nhiều fragment mới có thể chứa hết tam giác. nếu ở xa có thể chỉ cần một fragment cũng chứa hết vài tam giác.
- Bước 4: xử lý các fragment, mỗi fragment sẽ được tô màu, hoặc xử ánh sáng như đổ bóng
- Bước 5: kết hợp các hình vẽ, các vật thể sẽ được sắp đặt theo thứ tự trước sau, đôi khi ta phải vẽ vật thể nhiều lần để tạo hiệu ứng đặc biệt như blur các bản vẽ như vậy sẽ được lưu tạm vào đâu đó và sẽ được kết hợp tại bước này.
- Bước 6: vẽ khung hình hoàn chỉnh.
-
Bước 2 và bước 4 được sử lý bằng các đoạn code gọi là shader
- shader dùng cho bước 2 sẽ gọi là
Vertex shader
- shader dùng cho bước 4 sẽ gọi là
Fragment shader
- shader dùng cho bước 2 sẽ gọi là
Vertex Shader
- Dùng để thay đổi dữ liệu các đỉnh (như thay đổi vị trí)
Fragment Shader
- Dùng để sử lý dữ liệu của fragment (như thay đổi màu sắc) ví dụ như hiển thị object lên trước một object khác để tạo hiệu ứng nhìn xuyên thấu.
Material
- Được tạo nên bởi shader và bộ tham số của shader đó, mỗi material được coi là preset của shader đó
Shader được chia làm 2 nhóm
- Là:
- Unlit
- Lit
Unit
- Một shader được gọi là unlit nếu như nó không bị ảnh hưởng bởi các yếu tố về ánh sáng (tức là không tính tới ánh sáng khi tô màu) nên tốc độ dựng hình của nó rất nhanh
Lit
- Một shader được gọi là lit nếu như nó tính tới các yếu tố ánh sáng. các yếu tố phức tạp của ánh sáng thường được đơn giản hóa hoặc xấp xỉ bằng các phương trình hoặc công thức toán học
Phong shading
Disfuse
- Tính toán góc giữa tia tới và vecter pháp tuyến tại bề mặt tiếp xúc
- Nếu góc này càng nhỏ thì chứng tỏ bề mặt này đang hướng về phía nguồn sáng và nhận được nhiều ánh sáng hơn
- Nếu góc này càng lớn thì bề mặt nhận được ít ánh sáng hơn và chứng tỏ nó không quay về phía nguồn sáng và nó sẽ tối hơn
Specular
- Tính toán góc phản xạ và hướng của camera
Cel-Shading
- Là một shader dạng lit
- Vùng sáng và tối sẽ không chuyển tiếp một cách mượt mà thay vào đó là bị ngắt quãng
PBR
- Physically-Based Rendering
- Điều chỉnh các thông số ánh sáng bằng texture
- Nó không phải shader
PBR Texture
- Thuộc tính Albedo sẽ miêu tả hoa văn họa tiết màu sắc của vật thể mà không tính tới các yếu tố ánh sáng (specular hay shadow)
- Thuộc tính Smoothness (độ mịn) cho phép điều khiển độ mịn bề mặt vật thể, độ mịn càng cao thì ảnh phản xạ càng rõ vì tia sáng phản xạ đều hơn
- Thuộc tính Normal Map với mỗi điểm bất kỳ trên bề mặt theo thuật toán phong shading vector pháp tuyến là vector vuông góc với bề mặt tiếp tuyến tại điểm đấy gọi là normal vector
- Texture lưu trữ normal vector gọi là normal map. Normal map cũng có thể giúp mô hình low poly giống mô hình high poly
- Thuộc tính alpha điều khiên độ trong suốt
- Thuộc tính amient occlusion điều khiển độ tối của vùng biên hoặc tiếp giáp giữa 2 bề mặt
- Thuộc tính emission điều khiển vùng tự phát sáng (màu sắc cũng như cường độ sáng)
PBR Metalic
-
Metalic cũng giống specular đều điều khiển hình ảnh phản chiếu nhưng sử dụng bằng các cách khác nhau
-
Metallic điều khiển tỉ lệ pha trộn giữa albedo và hình ảnh phản chiếu từ môi trường xung quanh
-
Nếu chỉ số metallic gần 0 hơn thì màu của albedo sẽ đóng góp nhiều hơn vào màu sau cùng nhìn thấy
-
Nếu chỉ số metallic gần 1 hơn thì ảnh phản chiếu từ môi trường xung quanh sẽ chiếm yếu tố chủ đạo trong kết quả nhìn thấy sau cùng image
-
=> Không thể điều chỉnh màu của ảnh phản chiếu trực tiếp
PBR Specular
- Có toàn quyền điều chỉnh thông số của hình ảnh phản chiếu
Ngôn ngữ sử dụng
- CG (C cho đồ họa)
- ShaderLab
Ví dụ
Giờ chúng ta hãy tạo thử một shader unlit trong Unity
Đặt tên nó là DemoShader
Mặc định file unlit shader được tạo sẵn sẽ trông như thế này
Nhưng nó hơi dườm rà nên chúng ta sẽ đơn giản hóa nó bằng cách xóa đi những phần chưa cần xem xét
Chúng ta cùng phân tích qua nội dung file này
|
|
Thì ở đây “Unlit/DemoShader” là tên của shader
Khi bạn tạo material từ shader thì ta sẽ xác định theo tên này
Tiếp theo
|
|
Đoạn code này được bao bọc bởi Properties
để xác định rằng những khai báo trong này là để xác định thuộc tính hiển thị ra ngoài inspector
ở đây chúng ta có thuộc tính _MainTex
có kiểu dữ liệu là Texture
và ở dạng 2D. Giá trị được gán mặc định là null bằng cách sử dụng {} và màu mặc định là màu trắng.
Có nghĩa là ban đầu khi tạo material từ shader này trường Texture của material sẽ trống và màu mặc định của material khi không có texture được gán là màu trắng.
|
|
Ở đây định nghĩa thuộc tính Color
Chúng ta đến với khối lệnh tiếp theo là khối lệnh SubShader
|
|
Một shader có thể có nhiều khối lệnh SubShader khi chạy trên device thật nó sẽ duyệt subShader từ trên xuống dưới nếu gặp subShader nào tương thích nó sẽ sử dụng cái đấy thể sử dụng trong quá trình kết xuất.
Bạn cũng có thể chỉ định Fallback ở cuối để nếu không có subShader phù hợp thì sẽ sử dụng shader từ Fallback
Tags { "RenderType"="Opaque" }
sử dụng để chỉ định độ trong suốt hiển thị
Mọi xử lý sẽ nằm trong khối lệnh Pass
.
|
|
Ở đây chúng ta có cặp thẻ CGPROGRAM
và ENDCG
để xác định rằng chương trình sẽ chạy đoạn code bên được viết bên trong khối này.
#pragma vertex vert
cho biết rằng hàm có tên là vert
được khai báo là vertex shader
#pragma fragment frag
cho biết rằng hàm có tên là frag
được khai báo là fragment shader
Ngay cả khi bạn đang không ở trong PlayMode thì Unity cũng tự động gọi những hàm này nếu bạn định nghĩa nó.
#include "UnityCG.cginc"
bao gồm thư viện UnityCG.cginc chứa những phương thức hỗ trợ sẵn của unity để sử dụng.
fixed4 _Color;
để nhận giá trị từ thuộc tính Color từ khối lệnh Properties
struct v2f
xác định struct có tên là v2f là viết tắt của vertext to fragment được dùng để chuyển dữ liệu từ bước xử lý vertext (vertext shader
) sang bước xử lý fragment (fragment shader
)
v2f vert (appdata_base v)
là hàm khai báo đây là trình đổ bóng vertex, thông tin đỉnh được truyền dưới dạng tham số
v2f o;
Tạo đối tượng o của struct v2f đã khai báo trước đó.
o.pos = UnityObjectToClipPos(v.vertex);
Phương thức UnityObjectToClip được sử dụng để chuyển đổi tọa độ không gian 3D sang tọa độ 2D trên màn hình
fixed4 frag (v2f i) : COLOR
là hàm khai báo đây là trình đổ bóng fragment
Giờ chúng ta đã có một shader với thuộc tính color để thay đổi màu sắc