Tôi sẽ xây dựng nó thành một vài lớp để bạn có thể thấy nó kết hợp với nhau như thế nào.
Bắt đầu bằng cách tạo một shader mới trong Unity bằng cách chọn Create --> Shader --> Unlit
trong menu Tài sản hoặc menu ngữ cảnh nhấp chuột phải trong cửa sổ Dự án.
Trong khối trên cùng, chúng tôi sẽ thêm một _ScrollSpeeds
tham số để kiểm soát tốc độ di chuyển của kết cấu theo từng hướng:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}
Điều này cho thấy một thuộc tính float 4 thành phần mới trong trình kiểm tra vật liệu, với tên thân thiện "Tốc độ cuộn" (tương tự như thêm một public
hoặc Serialized
biến vào MonoBehaviour
tập lệnh)
Tiếp theo, chúng tôi sẽ sử dụng biến này trong trình tạo bóng Vertex để dịch chuyển tọa độ kết cấu ( o.uv
), bằng cách thêm chỉ hai dòng vào trình tạo bóng mặc định:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our new parameter here so it's visible to the CG shader
float4 _ScrollSpeeds;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
// Shift the uvs over time.
o.uv += _ScrollSpeeds * _Time.x;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
Tát nó trên một hình tứ giác (với kết cấu hươu cao cổ miễn phí đáng yêu của Kenney ) và bạn nhận được:
Để có được kết cấu cuộn ra ngoài trong một vòng, chúng ta chỉ cần sử dụng một lưới được chia nhỏ như một con nhện, với tọa độ uv v tăng dần từ tâm ra ngoài. Nhưng điều đó sẽ tự cung cấp cho một số đồ tạo tác hình răng cưa. Thay vào đó, tôi sẽ chỉ cho chúng ta cách tính UV của chúng trên mỗi mảnh.
Điều này tốn kém hơn một chút, cả vì các thao tác trig & length (đắt hơn toán cơ bản) và vì không hiệu quả để dự đoán & lưu trữ dữ liệu kết cấu trong phần cứng khi tính toán texcoords trên mỗi đoạn, so với việc chỉ nội suy chúng giữa các đỉnh. Nhưng đối với một hiệu ứng đặc biệt nhỏ như thế này, nó không quá mức.
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// Shift the UVs so (0, 0) is in the middle of the quad.
o.uv = v.uv - 0.5f;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// Convert our texture coordinates to polar form:
float2 polar = float2(
atan2(i.uv.y, i.uv.x)/(2.0f * 3.141592653589f), // angle
length(i.uv) // radius
);
// Apply texture scale
polar *= _MainTex_ST.xy;
// Scroll the texture over time.
polar += _ScrollSpeeds.xy * _Time.x;
// Sample using the polar coordinates, instead of the original uvs.
// Here I multiply by MainTex
fixed4 col = tex2D(_MainTex, polar);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
Điều đó mang lại cho chúng ta một cái gì đó như thế này (ở đây tôi đã tăng các tham số ốp lát trong vật liệu để rõ ràng hơn những gì đang xảy ra - chỉ bao gồm một lần lặp lại của gạch xung quanh vòng tròn trông có vẻ méo mó và kỳ lạ)
Cuối cùng, để tô màu cho kết cấu bằng một gradient cuộn, chúng ta chỉ cần thêm gradient dưới dạng kết cấu thứ hai và nhân chúng lại với nhau.
Đầu tiên chúng ta thêm tham số kết cấu mới ở trên cùng:
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_TintTex("Tint Texture", 2D) = "white" {}
_ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}
Và khai báo nó trong khối CGPROGRAM của chúng tôi để trình tạo bóng CG có thể nhìn thấy nó:
sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;
float4 _ScrollSpeeds;
Sau đó cập nhật shader mảnh của chúng tôi để sử dụng cả hai kết cấu:
fixed4 frag(v2f i) : SV_Target
{
float2 polar = float2(
atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
length(i.uv) // radius
);
// Copy the polar coordinates before we scale & shift them,
// so we can scale & shift the tint texture independently.
float2 tintUVs = polar * _TintTex_ST.xy;
tintUVs += _ScrollSpeeds.zw * _Time.x;
polar *= _MainTex_ST.xy;
polar += _ScrollSpeeds.xy * _Time.x;
fixed4 col = tex2D(_MainTex, polar);
// Tint the colour by our second texture.
col *= tex2D(_TintTex, tintUVs);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
Và bây giờ hươu cao cổ của chúng tôi thực sự trippy:
Với một lựa chọn nghệ thuật hơn một chút về kết cấu và tốc độ cuộn, điều này có thể tạo ra một hiệu ứng khá giống với hiệu ứng được hiển thị trong câu hỏi.
Bạn có thể nhận thấy hai hiện vật nhỏ với phiên bản tôi đã trình bày ở trên:
Các khuôn mặt gần trung tâm của vòng tròn bị kéo dài / gầy / nhọn, sau đó khi chúng di chuyển ra bên ngoài, chúng sẽ bị bẹp / rộng.
Sự biến dạng này xảy ra bởi vì chúng ta có một số mặt cố định xung quanh chu vi, nhưng chu vi chúng trải rộng sẽ rộng hơn khi bán kính tăng lên, trong khi chiều cao của chúng vẫn giữ nguyên.
Chúng ta có thể khắc phục điều này bằng cách ánh xạ lại thành phần dọc của mẫu kết cấu để đi theo đường cong logarit, do đó, sự lặp lại của kết cấu cách xa nhau hơn khi bán kính tăng và gần nhau hơn về phía trung tâm. (Trên thực tế, điều này mang lại cho chúng ta một hồi quy vô hạn của hươu cao cổ nhỏ hơn và nhỏ hơn ...)
Có một hàng gồm một hoặc hai pixel mờ dọc ở giữa bên trái của hình tứ giác.
Điều này xảy ra bởi vì GPU nhìn vào hai tọa độ mẫu kết cấu liền kề để tìm ra cách lọc nào sẽ sử dụng. Khi các mẫu gần nhau, nó cho biết kết cấu đang được hiển thị lớn / gần và hiển thị mức mip chi tiết nhất. Khi các mẫu cách xa nhau, nó đoán chúng ta phải hiển thị họa tiết ở mức thu phóng nhỏ hoặc ở xa, và nó lấy mẫu từ một mipmap nhỏ hơn / mờ để đảm bảo chúng ta không có các tạo tác răng cưa lấp lánh.
Vấn đề là ở đây, chúng ta đang ở điểm bao quanh trong tọa độ cực, từ -180 đến 180 độ. Vì vậy, chúng tôi thực sự lấy mẫu từ các điểm rất giống nhau trong không gian kết cấu lặp lại của chúng tôi, ngay cả khi tọa độ số của chúng làm cho chúng trông giống nhau. Vì vậy, chúng tôi có thể cung cấp các vectơ gradient lấy mẫu của riêng mình để sửa lỗi này.
Đây là một phiên bản với những sửa đổi sau:
fixed4 frag(v2f i) : SV_Target
{
float2 polar = float2(
atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
log(dot(i.uv, i.uv)) * 0.5f // log-radius
);
// Check how much our texture sampling point changes between
// neighbouring pixels to the sides (ddx) and above/below (ddy)
float4 gradient = float4(ddx(polar), ddy(polar));
// If our angle wraps around between adjacent samples,
// discard one full rotation from its value and keep the fraction.
gradient.xz = frac(gradient.xz + 1.5f) - 0.5f;
// Copy the polar coordinates before we scale & shift them,
// so we can scale & shift the tint texture independently.
float2 tintUVs = polar * _TintTex_ST.xy;
tintUVs += _ScrollSpeeds.zw * _Time.x;
polar *= _MainTex_ST.xy;
polar += _ScrollSpeeds.xy * _Time.x;
// Sample with our custom gradients.
fixed4 col = tex2Dgrad(_MainTex, polar,
_MainTex_ST.xy * gradient.xy,
_MainTex_ST.xy * gradient.zw
);
// Since our tint texture has its own scale,
// its gradients also need to be scaled to match.
col *= tex2Dgrad(_TintTex, tintUVs,
_TintTex_ST.xy * gradient.xy,
_TintTex_ST.xy * gradient.zw
);
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
_Time
biến tích hợp, bạn có thể thêm vào tọa độ kết cấu của mình trong trình tạo bóng (đỉnh) để có được hiệu ứng cuộn giá rẻ. Hiệu ứng hình lục giác cũng khá đơn giản. Nếu bạn chỉnh sửa câu hỏi của mình để làm nổi bật chỉ một hiệu ứng và hỏi "làm thế nào tôi thực hiện điều này trong trình tạo bóng Unity", chúng tôi có thể giúp bạn giải quyết. Hãy chắc chắn để xác định xem trình đổ bóng có cần phản ứng với ánh sáng / bóng không, vì điều đó có tác động đến cách viết.