Làm thế nào để thực hiện thay đổi màu mảnh shader?


7

Tôi có một nền tảng của một kích thước nhất định và chứa đầy một màu nhất định. Tôi muốn thay đổi nó bằng hiệu ứng hoạt hình, bắt đầu từ trung tâm và trải ra cho đến khi nó mở rộng toàn bộ nền. Màu mới sẽ nhạt dần / hòa trộn mịn với màu hiện có từ nền. Một số loại gradient xuyên tâm thay đổi màu sắc và sau đó trải ra trên toàn bộ nền.

Tôi đang làm việc với SpriteKit trên iOS và tôi thực sự chắc chắn rằng cách tốt nhất để thực hiện điều này là thực hiện điều này với các shader mảnh vỡ mới đối với SDK SpriteKit iOS 8. Tôi đã thực hiện một số công việc với shader và hiểu cách chúng hoạt động nhưng tôi đang yêu cầu trợ giúp thêm về toán học đằng sau điều này.


Bạn đã thử gì?
Concept3d

Chưa có gì. Chỉ làm việc với một số Blooming-FX và một số "Plasma FX" nhưng tôi thực sự không thể biết được điểm khởi đầu cho việc này ...
Simon Kemper

Bạn có thể thấy hữu ích câu hỏi của tôi và câu trả lời của nó trên stackoverflow. Nó bao gồm cách thực hiện gradient gần như hoàn hảo (tuyến tính, xuyên tâm, elip, chuyển vị, chia tỷ lệ và xoay tùy thuộc vào đầu vào). Cũng có thể được mở rộng đến nhiều điểm dừng.
wonderra

Cũng là một nguồn tốt để có được một điểm để bắt đầu từ! Cảm ơn!
Simon Kemper

Câu trả lời:


11

Đây là cách tôi sẽ làm điều này. Trước tiên, hãy chắc chắn rằng bạn có UVs hoặc coords thế giới của đối tượng (mà bạn có thể đi qua từ shader đỉnh của bạn) có sẵn cho bạn. Nếu nó chỉ là một nền tảng, bạn cũng có thể chỉ sử dụng các đoạn coord ( gl_FragCoord).

Ví dụ, giả sử chúng ta đang sử dụng các tia UV. Một shader mảnh chỉ với: gl_FragColor = vec4(vec3(uv.x),1.0);sẽ trông giống như:

X UV

Và tương tự, gl_FragColor = vec4(vec3(uv.y),1.0);sẽ giống như:

Y UV

Nếu bạn đang sử dụng hợp đồng thế giới hoặc hợp đồng phân đoạn, bạn vẫn có thể chia tỷ lệ và dịch để bình thường hóa thành (0,0) -> (1, 1), nhưng dù sao chúng ta cũng cần phải mở rộng quy mô và dịch lại để bạn có thể bỏ qua bước đó Thay vào đó, các coords của bạn nằm trong khoảng từ (-1.0, -1.0) đến (1.0, 1.0) để làm cho các bước tiếp theo dễ dàng hơn, như vậy:uv = 2.0 * uv - 1.0;

Bây giờ trước khi chúng ta tiến xa hơn, hãy tạo một gradient xuyên tâm siêu đơn giản. Hãy nhớ rằng phương trình của một đường tròn là x² + y² = r². Vì vậy, sử dụng các UV biến đổi như một hệ tọa độ, một vòng tròn đơn vị (r = 1) có thể được tạo ra gl_FragColor = vec4(vec3(uv.x * uv.x + uv.y * uv.y),1.0);và sẽ trông giống như:

R UV

Bước tiếp theo là hoạt hình. Để làm điều này, bạn chỉ cần chơi xung quanh với giá trị "r" và thay đổi bán kính. Bạn không cần phải lo lắng về việc kẹp các giá trị màu giữa 0,0 và 1,0 vì dù sao điều đó cũng tự động xảy ra.

Tất cả cùng nhau nó sẽ trông giống như thế này:

uniform vec2 screenSize;
uniform float time;

void main(void) {
    // Scale to UV space coords
    vec2 uv = gl_FragCoord.xy / screenSize;

    // Transform to [(-1.0, -1.0), (1.0, 1.0)] range
    uv = 2.0 * uv - 1.0;

    // Have something to vary the radius (can also just be a linear counter (time))
    float wave = sin(time);

    // Calculate how near to the center (0.0) or edge (1.0) this fragment is
    float circle = uv.x * uv.x + uv.y * uv.y;

    // Put it all together
    gl_FragColor = vec4(vec3(circle + wave),1.0);
}

Tất nhiên bạn có thể đơn giản hóa rất nhiều thứ và chơi xung quanh nhiều hơn với các biến và hằng. Đây là bản demo trực tiếp của tôi đang làm điều đó: https://www.shadertoy.com/view/4sjXWh

Biên tập:

Nếu bạn muốn làm điều này với các màu sắc khác nhau, may mắn thay, có một giải pháp dễ dàng! Chuyển màu của bạn dưới dạng đồng phục (hoặc mã hóa chúng trong shader mảnh thực tế) và thay đổi dòng cuối cùng thành này:

gl_FragColor = mix(color1, color2, circle + wave);

color1color2đều là vec4s. Đây là hành động: https://www.shadertoy.com/view/XsjSW1


1
Xuất sắc! Đó là một điểm tốt cho tôi để bắt đầu! Nhưng thay vì tạo hiệu ứng cho gradient trong một vòng lặp, tôi cần một gradient mới mỗi khi tôi thay đổi màu sắc và bắt đầu hoạt hình đó trên đỉnh của gradient cũ. Giống như sóng màu gradient. Tôi nghĩ rằng tôi sẽ sử dụng shader này như một số loại mặt nạ và áp dụng nó trên các họa tiết. Và mỗi khi tôi thay đổi màu sắc, tôi lại thêm một sprite mới với màu đó lên trên ...
Simon Kemper

1
Không chắc chắn hai ví dụ đầu tiên này có thể là gl_FragColor = vec4 (vec3 (uv.x), 1.0); ? (Không quá cầu kỳ - câu trả lời này cũng giúp tôi!)

@Poldie Bạn hoàn toàn đúng; đó là một lỗi đánh máy Đã sửa!
Yousef Amar

@SimonKemper Bạn cũng có thể chuyển các màu bạn muốn làm đồng phục và thay đổi chúng khi cần khi một chu kỳ hoạt hình kết thúc (bằng cách kiểm tra timebiến bên ngoài shader). Tôi đã thêm một chút vào câu trả lời của mình để cho bạn thấy ý tôi là gì. Hy vọng bạn thấy rằng hữu ích!
Yousef Amar

2

Vì tôi đang làm việc với SpriteKit trên iOS, nên trình hỗ trợ đổ bóng trong SpriteKit vẫn còn thiếu một số điều để thực hiện trình đổ bóng màu hoạt hình trong SpriteKit.

Tuy nhiên, đối với những người gặp phải tình huống tương tự, tôi đã tìm thấy một cách hiệu quả hơn để tạo độ dốc hoạt hình bằng bộ sprite. Thay vì làm việc với các shader tôi đã tạo ra một hình ảnh đơn giản với một vòng tròn mờ ... Giống như thế này:

độ dốc sprite

và với một vài dòng SpriteKit-Code, bạn sẽ có được các sóng màu gradient được pha trộn đẹp và mượt mà bắt đầu từ trung tâm và mở rộng ra khắp màn hình!

// Create a Sprite with Gradient-Texture
SKSpriteNode* gradientSpriteNode = [SKSpriteNode spriteNodeWithImageNamed:@"SSGradientMap.png"];

// Set the color and scale properties
gradientSpriteNode.color = color;
gradientSpriteNode.colorBlendFactor = 1.0;
[gradientSpriteNode setScale:0.1];

// Add it to our parent node
[self addChild:gradientSpriteNode];

// Add it to our container
[self.gradientNodes addObject:gradientSpriteNode];

// Create and configure a scale up action
SKAction* scaleAction = [SKAction scaleTo:3.0 duration:3.0];
scaleAction.timingMode = SKActionTimingEaseInEaseOut;

// Run that action and attach a completion block
[gradientSpriteNode runAction:scaleAction completion:^{

    // Create a random color
    float r = [SSHelper randomWithLowerFloat:0. andUpperFloat:1.];
    float g = [SSHelper randomWithLowerFloat:0. andUpperFloat:1.];
    float b = [SSHelper randomWithLowerFloat:0. andUpperFloat:1.];
    UIColor* randomColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0];

    // Create a new gradient ontop of the last one
    [self addGradientWithColor:randomColor];

    // If more than 2 gradient sprites inside our container the last one can be removed
    if (self.gradientNodes.count > 2) {
        SKSpriteNode* gradientNode_ = [self.gradientNodes firstObject];
        [gradientNode_ removeFromParent];
        [self.gradientNodes removeObject:gradientNode_];
    }
}];

Ý tưởng là thêm sprite vào màn hình nhưng thu nhỏ lại bằng không. Một hình ảnh động mở rộng nó cho đến khi nó mở rộng toàn bộ màn hình. Sau đó, bạn bắt đầu lại nhưng có sprite pha trộn với một màu khác.

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.