Как использовать альфа-компонент в цветах градиента с GKNoise и SKTexture?

Я использую GKNoise с картой градиента для генерации цветового шума, получая CGImage через SKTexture в Mac OS. В частности, я использую GKPerlinNoiseSource и устанавливаю два цвета градиента со значениями -1.0 и 1.0.

Это работает должным образом, если два цвета непрозрачны. Если у одного или обоих цветов альфа-компонент меньше 1.0, я ожидаю, что результат будет прозрачным. Однако мне кажется, что альфа полностью игнорируется во вводе цвета градиента GKNoise (обрабатывается так, как будто он всегда 1.0) - или, если не игнорируется там, игнорируется при рендеринге SKTexture выходного изображения.

Я нашел пару вопросов SO, которые ссылаются на ограничения SKTexture в других целях, включая возможное обходное решение с рендерингом SKView, однако здесь это не применяется, потому что я использую только экземпляр SKTexture для получения CGImage (без использования SKView или в противном случае используя что-либо из SpriteKit) - и FWIW эти вопросы сосредоточены на iOS. Для справки:

Я ищу идеи, как заставить работать альфа-компоненты в цветах градиента с помощью GKNoise / SKTexture.

Ниже приведено тестовое изображение вывода и код, который его воспроизводит. В представлении оба изображения CGImage отрисовываются одинаково; Я ожидаю, что тот, что нарисован с красной альфа = 0,5, будет темнее в красных частях, когда фон черный, светлее, когда он белый, и т. Д.

снимок экрана вывода изображения текстового кода

import Foundation
import GameplayKit

class GKNoiseGradientIssue {
    
    var redColor_opaque = NSColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0)
    var redColor_halfAlpha = NSColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.5)
    
    var blueColor_opaque = NSColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0)
    
    var opaqueImage: CGImage
    var halfAlphaImage: CGImage
    
    init() {
        let noiseSource = GKPerlinNoiseSource(frequency: 0.15, 
                                               octaveCount: 7, 
                                               persistence: 1.25, 
                                               lacunarity: 0.5, 
                                               seed: 12345)
    
        let opaqueGradient: [NSNumber: NSColor] = [-1.0: redColor_opaque, 1.0: blueColor_opaque]
        let opaqueNoise = GKNoise(noiseSource, gradientColors: opaqueGradient)
        let opaqueNoiseMap = GKNoiseMap(opaqueNoise,
                                         size: [200.0, 200.0],
                                         origin: [0.0, 0.0],
                                         sampleCount: [200, 200],
                                         seamless: true)
        let opaqueTexture = SKTexture(noiseMap: opaqueNoiseMap)
        self.opaqueImage = opaqueTexture.cgImage()
        
        
        let halfAlphaGradient: [NSNumber: NSColor] = [-1.0: redColor_halfAlpha, 1.0: blueColor_opaque]
        let halfAlphaNoise = GKNoise(noiseSource, gradientColors: halfAlphaGradient)
        let halfAlphaNoiseMap = GKNoiseMap(halfAlphaNoise,
                                                size: [200.0, 200.0],
                                                origin: [0.0, 0.0],
                                                sampleCount: [200, 200],
                                                seamless: true)
        let halfAlphaTexture = SKTexture(noiseMap: halfAlphaNoiseMap)
        self.halfAlphaImage = halfAlphaTexture.cgImage()
    }
}

class GradientIssueView: NSView {
    var issue: GKNoiseGradientIssue?
    
    override func awakeFromNib() {
        self.issue = GKNoiseGradientIssue()
    }
    
    override func draw(_ dirtyRect: NSRect) {
        NSColor.black.setFill()
        self.bounds.fill()
        
        if let cgc = NSGraphicsContext.current?.cgContext {
            cgc.draw(self.issue!.opaqueImage, 
                     in: CGRect(origin: CGPoint(x: 10.0, y: 10.0), 
                                size: CGSize(width: 200.0, height: 200.0)))
            
            cgc.draw(self.issue!.halfAlphaImage, 
            in: CGRect(origin: CGPoint(x: 10.0, y: 220.0), 
                       size: CGSize(width: 200.0, height: 200.0)))
        }
    }
}

person Corbell    schedule 24.07.2020    source источник


Ответы (1)


Просто продолжайте здесь, для всех, кто может столкнуться с этим ... Хотя я так и не получил окончательного ответа от форумов разработчиков Apple, я пришел к выводу, что SKTexture + GKNoise поддерживает только непрозрачные цвета, поэтому на заданный вопрос нет ответа: этого нельзя сделать с SKTexture. Вместо этого я обновил свой подход, чтобы просто использовать GKNoiseMap напрямую и вычислять собственные градиенты.

Преимущество этого подхода в том, что теперь я могу создавать градиенты из источника шума, используя более двух цветов. У меня также есть больше возможностей для творческого искажения или ограничения значений шума перед их преобразованием в цвета.

Чтобы использовать GKNoiseMap напрямую, а не с двухцветными градиентами и подходом SKTexture, создайте шум без привязки к цветам градиента и создайте GKNoiseMap таким же образом:

let myNoise = GKNoise(noiseSource)
let noiseMap = GKNoiseMap(myNoise, 
                          size: [myModelWidth, myModelHeight],
                          origin: [myX, myY],
                          sampleCount: [myPointWidth, myPointHeight],
                          seamless: true)

Затем при рендеринге просто вызовите метод noiseMap value (), чтобы получить значение шума для каждой точки в вашем представлении или выводе изображения, а затем вычислите свой собственный цветовой градиент для этой точки, включая альфа-канал (при желании). Обратите внимание, что шум Вороного будет между 0,0 и 1,0, в то время как другой шум на основе перлина будет между -1,0 и 1,0; Чтобы упростить здесь, я предполагаю, что кому-то просто нужен градиент между двумя цветами в 0,0 и 1,0.

Учитывая два цвета в цветовом пространстве .deviceRGB (rgbColor1 и rgbColor2) и значение шума unitNoiseValue как CGFloat,

let r_range = rgbColor2.redComponent - rgbColor1.redComponent
let g_range = rgbColor2.greenComponent - rgbColor1.greenComponent
let b_range = rgbColor2.blueComponent - rgbColor1.blueComponent
let a_range = rgbColor2.alphaComponent - rgbColor1.alphaComponent
        
let r = noiseUnitValue * r_range + rgbColor1.redComponent
let g = noiseUnitValue * g_range + rgbColor1.greenComponent
let b = noiseUnitValue * b_range + rgbColor1.blueComponent
let a = noiseUnitValue * a_range + rgbColor2.alphaComponent
        
return NSColor(red: r, green: g, blue: b, alpha: a)

Для обеспечения такой производительности может быть полезно ограничить дискретные уровни каждого градиента (например, 256 уровней), а также кэшировать вычисленные цвета для больших изображений и хранить в хэше, который можно быстро найти, и аналогичные методы. Кроме того, поскольку преобразование здесь просто отображает единичное значение, вы можете при желании выполнить другое искажение вашего значения шума перед вычислением цвета градиента, например дополнительное затухание или нормализацию или другое «формирование» входного значения.

person Corbell    schedule 12.11.2020