Shadow hay còn gọi là bóng thường được sử dụng nhằm mục đích khiến giúp tạo chiều sâu cho khung hình cũng như liên kết
nhân vật vào không gian.
Shadow sử dụng trong 2D thường là shadow fake (ở dạng sprite cố định tùy game mà có thể thêm thắt việc thay đổi scale của sprite fake shadow để trông thật hơn).
Còn trong môi trường 3D chúng ta có thể hiển thị shadow của vật thể lên một bề mặt.
- Tạo một scene trống ở mode 3D (có sẵn camera và lighting), light được tạo sẵn là
Directional Light
và bật Shadow Type được đặt sẵn làSoft Shadows
, nếu bạn tạo Light bằng tay mà không thấy có shadow hãy nhớ đổi shadow type của light từNo Shadows
sang giá trị khác
- Tạo background sử dụng Image,
Render Mode
của canvas được đặt thànhScreen Space - Camera
- Tạo một
Cube
mặc định (thuộc tínhCast Shadows
được đặt làOn
)
Như bạn thấy chúng ta không thể hiển thị shadow của Cube lên background ở dạng UI
- Tạo một tấm
Plane
để nhận shadow từCube
như bạn thấy chúng ta có tùy chọnReceive Shadows
ởLighting
củaMesh Renderer
KhiReceive Shadows
được bật thìMesh Renderer
sẽ có thể nhận được shadow từ vật thể khác chiếu lên nó, còn nếuReceive Shadows
bị tắt thì sẽ không có shadow nào hiển thị được trênMesh Renderer
này cả.
Ở đây Plane
không cần hiển thị shadow của nó lên vật thể khác vì vậy chúng ta chuyển Cast Shadows
của nó thành Off
Nhưng vấn đề ở đây là shadow của Cube
chỉ được chiếu lên Plane
nếu chúng ta tắt Mesh Renderer
của Plane
để nó
không chắn
tầm hiển thị của background thì chúng ta lại không thể nhìn thấy shadow của cube được nữa.
Vậy phải làm thế nào?
Làm thế nào mà Plane
trong suốt mà vẫn nhận được shadow?
Hmmm chúng ta sẽ cần viết shader tùy chỉnh cho Plane để thực hiện được việc này.
- Đầu tiên chúng ta cần tạo một shader
Unlit
Đặt tên nó là ReceiveShadow
Ban đầu nó trông như thế này
|
|
Chúng ta sẽ loại bỏ phần xử lý fog trong shader này
|
|
Chúng ta chỉ chỉnh sửa color và không cần thay đổi texture nên Properties sẽ chỉ khai báo _Color
thay vì khai
báo _MainTex
Đoạn shader bên trên sẽ thay đổi thành
|
|
Vì là shader unlit nên nó không bị ảnh hưởng bởi các yếu tố về ánh sáng, giờ chúng ta sẽ sửa lại nó để nó hỗ trợ nhận ánh sáng
|
|
Thêm vào Tags "LightMode"="ForwardBase"
để cho phép nhận ánh sáng
Chúng ta sẽ pha trộn màu ở kênh alpha cho SubShader bằng câu lệnh Blend
|
|
Ngoài câu lệnh blend này chúng ta có một số cú pháp phổ biến như sau
|
|
Xem thêm thông tin về cách hoạt động của Blend ở đây
|
|
Việc triển khai hỗ trợ việc receive shadows sẽ yêu cầu biên dịch đường truyền ánh sáng base thành một số biến thể để xử
lý directional light without shadows
và directional light with shadows
một cách chính xác
#pragma multi_compile_fwdbase
chỉ định shader này là một shader biến thể của của multi_compile_fwdbase#include "AutoLight.cginc"
bao gồm thư viện AutoLight.cginc để sử dụng các hỗ trợ về ánh sáng từ thư viện này ( SHADOW_COORDS, TRANSFER_SHADOW, SHADOW_ATTENUATION)
Giá trị truyền từ vertex sang fragment là
|
|
Ở đây thì SHADOW_COORDS
là một macro của thư viện AutoLight.cginc
nó sẽ đưa dữ liệu của shadow vào trong TEXCOORD0
trong hàm xử lý vertex chúng ta có
|
|
Hàm TRANSFER_SHADOW(o)
được gọi để tính toán dữ liệu shadow
Trong hàm xử lý fragment chúng ta có
|
|
LIGHT_ATTENUATION(i)
tính toán độ suy giảm ánh sáng do sử dụng blend theo alpha nên độ biến đổi ánh sáng phụ thuộc vào
alpha
Shadow càng thì giá trị càng gần 1 vì vậy cần đảo ngược giá trị của LIGHT_ATTENUATION
Shader hoàn chỉnh sẽ trông như này
|
|
- Tạo material từ shader vừa viết và gắn nó cho
Plane
lúc nàyPlane
sẽ hứng được shadow củaCube
Nhưng khoan kết quả vẫn không như chúng ta mong đợi background bị cắt mất phần của tấm plane. Shader của tấm plane ZWrite không bị tắt để có thể nhận shadow, do đó các đối tượng như image luôn được draw sau các đối tượng opaque và nó sẽ bị bỏ qua sau khi vẽ plane.
Vì vậy chúng ta sẽ thay đổi Render Queue của Image background
- Tạo một shader UI/Default và chỉnh Render Queue của nó thành 1950 (nhỏ hơn 2000)
Cuối cùng kết quả của chúng ta thu được như sau: