Hoạ tiết hoạt hình cho các mô hình; Làm thế nào để viết một shader?


9

Đang xem một số Rocket League và nhận thấy có đề can và bánh xe hoạt hình.

Hình ảnh.

Tôi muốn thực hiện một cái gì đó tương tự như các hiệu ứng trong hình trên.

Làm thế nào tôi có thể viết về Unity Shader để tạo hiệu ứng bánh xe?

Tôi không biết nhiều về Shader, nhưng tôi có thể chỉnh sửa Unity Shader tiêu chuẩn để thực hiện hiệu ứng hoạt hình không?


3
Shader chắc chắn là giải pháp phù hợp ở đây, ngay cả đối với một thứ đơn giản như một kết cấu cuộn. Unity có một _Timebiế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.
DMGregory

Cảm ơn bạn về thông tin. Đã thay đổi câu hỏi của tôi, điều đó tốt hơn một chút? Tôi thực sự không biết về ánh sáng và bóng tối, vì vậy tôi đã bỏ qua nó cho đến khi tôi hiểu rõ hơn về Shader. Tôi đoán tôi sẽ muốn chúng, nhưng tôi chỉ có thể sao chép các phần từ trình đổ bóng tiêu chuẩn phải không?
JacketPotatoeFan

3
Tôi sẽ mở rộng câu trả lời vào tối nay một chút (mặc dù cảm thấy thoải mái khi đánh bại tôi, nếu có ai muốn trả lời ngay bây giờ!) - nhưng bạn thường viết một shader Unity sử dụng ánh sáng làm "Surface Shader" trong khi một cái không cần ánh sáng thường đơn giản hơn để viết dưới dạng "Shit Unlit". Đối với tôi có vẻ như những bánh xe đó là không sáng (bóng hơi ở rìa trông giống như SSAO), vì vậy tôi sẽ để ánh sáng ra khỏi hình ảnh cho người mới bắt đầu. Bạn có thể làm rõ: bạn có muốn chính xác mô hình hình học được hiển thị ở đây, hoặc khả năng cuộn các kết cấu tùy ý ra bên ngoài & cầu vồng cho chúng như thế không?
DMGregory

Cảm ơn rất nhiều, bất cứ thông tin nào bạn có thể cung cấp để giúp tôi bắt đầu sẽ rất tuyệt. Tôi thực sự ngưỡng mộ những người có thể viết shader, tôi đã xem qua một vài bài viết cho Unity Shader, và vâng, rất khó hiểu. Tôi nghĩ chỉ cần cuộn qua kết cấu (hoặc uv kết cấu?) Sẽ đủ tốt và với khả năng pha màu.
JacketPotatoeFan

Câu trả lời:


13

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 --> Unlittrong 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 _ScrollSpeedstham 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 publichoặc Serializedbiến vào MonoBehaviourtậ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:

Một quad với một con hươu cao cổ lặp đi lặp lại di chuyển qua nó.

Để 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ạ)

Con hươu cao cổ lát gạch tỏa ra từ trung tâm của một hình tứ

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:

xa lắm, anh bạn ....

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;
}

2
Một vài cải tiến nhỏ người ta có thể thực hiện: 1) sử dụng đường cong logarit cho hướng v giúp kết cấu giữ nguyên hình dạng khi cuộn ra ngoài (thay vì thu hẹp đến một điểm ở giữa, bạn chỉ cần lấy một chuỗi vô hạn các bản sao nhỏ hơn cho đến khi mip làm mờ nó đi) 2) tính toán độ dốc kết cấu của riêng bạn và sử dụng tex2Dgradsửa lỗi tạo tác lọc trong đó góc bao quanh từ -pi đến + pi (đó là dòng pixel mờ ở bên trái). Đây là kết quả cảm động với nhiều màu xanh hơn
DMGregory

Thật tuyệt vời, cảm ơn vì sự đổ vỡ là tốt. Tôi cảm thấy Shader là thứ mà tôi không bao giờ có thể viết được (đặc biệt là những thứ như những gì bạn đã làm cho "cực", những thứ toán học chẳng có ý nghĩa gì với tôi, vì tôi chỉ có những kỹ năng toán học rất cơ bản).
JacketPotatoeFan

1
Những thứ như thế mọi người thường chỉ cần nhìn lên. ;) Tôi chỉ làm nó đủ để nó lăn khỏi bàn phím. Hãy thử chơi xung quanh với các chức năng toán học khác nhau và xem kết quả - có một công cụ như thế này để giúp tôi hình dung những gì toán học đang làm về mặt hình học là cách tôi bắt đầu thực hành ngay từ đầu. (Không phải với các shader cụ thể, mà là một trình hiển thị WinAmp cũ có tên WhiteCap ...) Ngay cả khi toán shader bị sai một cách khủng khiếp, nó thường rất tuyệt khi nhìn vào. : D
DMGregory
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.