Как вы воспроизводите видео с альфа-каналом с помощью AVFoundation?

у меня есть приложение AR, которое использует SceneKit, и импортирует видео на сцену с помощью AVPlayer и тем самым добавляя его в качестве дочернего узла SKVideo узел.

видео видно, как это должно быть, но прозрачность в видео не достигается.

код следующим образом:

let spriteKitScene = SKScene(size: CGSize(width: self.sceneView.frame.width, height: self.sceneView.frame.height))
spriteKitScene.scaleMode = .aspectFit

guard let fileURL = Bundle.main.url(forResource: "Triple_Tap_1", withExtension: "mp4") else {
    return
}

let videoPlayer = AVPlayer(url: fileURL)
videoPlayer.actionAtItemEnd = .none

let videoSpriteKitNode = SKVideoNode(avPlayer: videoPlayer)
videoSpriteKitNode.position = CGPoint(x: spriteKitScene.size.width / 2.0, y: spriteKitScene.size.height / 2.0)
videoSpriteKitNode.size = spriteKitScene.size
videoSpriteKitNode.yScale = -1.0
videoSpriteKitNode.play()
spriteKitScene.backgroundColor = .clear          
spriteKitScene.addChild(videoSpriteKitNode)

let background = SCNPlane(width: CGFloat(2), height: CGFloat(2))
background.firstMaterial?.diffuse.contents = spriteKitScene

let backgroundNode = SCNNode(geometry: background)
backgroundNode.position = position
backgroundNode.constraints = [SCNBillboardConstraint()]
backgroundNode.rotation.z = 0
self.sceneView.scene.rootNode.addChildNode(backgroundNode)

// Create a transform with a translation of 0.2 meters in front of the camera.
var translation = matrix_identity_float4x4
translation.columns.3.z = -0.2
let transform = simd_mul((self.session.currentFrame?.camera.transform)!, translation)

// Add a new anchor to the session.
let anchor = ARAnchor(transform: transform)
self.sceneView.session.add(anchor: anchor)

каким может быть лучший способ реализовать прозрачность Triple_Tap_1 видео в этом случае. Я прошел через некоторые вопросы переполнения стека по этой теме, и найдено единственное решение-репозиторий KittyBoom, который был создан где-то в 2013 году, используя Objective C.

Я надеюсь, что сообщество может выявить лучшее решение для этой проблемы. GPUImage библиотека-это не то, что я мог приступить к работе.

1 ответов


я придумал два способа сделать это возможным. Оба используют модификаторы шейдеров поверхности. Подробную информацию о модификаторах шейдеров можно найти в Документация Для Разработчиков Apple.

здесь пример проекта я создал.


1. Маскировка

  1. вам нужно будет создать другое видео, которое представляет собой маску прозрачности. В этом видео черный = полностью непрозрачный, белый = полностью прозрачный (или любой другой способ представления прозрачности, вам просто нужно будет поработать с шейдером поверхности).

  2. создать SKScene С этим видео так же, как вы делаете в коде, который вы предоставили, и поместите его в material.transparent.contents (тот же материал, в который вы помещаете диффузное содержимое видео)

    let spriteKitOpaqueScene = SKScene(...)
    let spriteKitMaskScene = SKScene(...)
    ... // creating SKVideoNodes and AVPlayers for each video etc
    
    let material = SCNMaterial()
    material.diffuse.contents = spriteKitOpaqueScene
    material.transparent.contents = spriteKitMaskScene
    
    let background = SCNPlane(...)
    background.materials = [material]
    
  3. добавьте модификатор шейдера поверхности в материал. Он собирается "конвертировать" черный цвет из видео маски (ну, на самом деле красный цвет, так как нам нужен только один цветовой компонент) в Альфа.

    let surfaceShader = "_surface.transparent.a = 1 - _surface.transparent.r;"
    material.shaderModifiers = [ .surface: surfaceShader ]
    

вот именно! Теперь белый цвет на маскирующем видео будет прозрачным на плоскости.

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

плюсы:

  • нет артефактов (если синхронизировать)
  • точный

плюсы:

  • требуются два видео вместо одного
  • требуется синхронизация AVPlayers

2. Chroma keying

  1. вам нужно видео, которое имеет яркий цвет фон, который будет представлять части, которые должны быть прозрачными. Обычно используется зеленый или пурпурный.

  2. создать SKScene для этого видео, как обычно, и положил его в material.diffuse.contents.

  3. добавьте модификатор шейдера поверхности цветности, который вырежет цвет по вашему выбору и сделает эти области прозрачными. Я одолжил это!--82-->шейдер из GPUImage и я действительно не знаю, как это на самом деле работает. Но это, кажется, объяснено в ответ.

     let surfaceShader =
    """
    uniform vec3 c_colorToReplace = vec3(0, 1, 0);
    uniform float c_thresholdSensitivity = 0.05;
    uniform float c_smoothing = 0.0;
    
    #pragma transparent
    #pragma body
    
    vec3 textureColor = _surface.diffuse.rgb;
    
    float maskY = 0.2989 * c_colorToReplace.r + 0.5866 * c_colorToReplace.g + 0.1145 * c_colorToReplace.b;
    float maskCr = 0.7132 * (c_colorToReplace.r - maskY);
    float maskCb = 0.5647 * (c_colorToReplace.b - maskY);
    
    float Y = 0.2989 * textureColor.r + 0.5866 * textureColor.g + 0.1145 * textureColor.b;
    float Cr = 0.7132 * (textureColor.r - Y);
    float Cb = 0.5647 * (textureColor.b - Y);
    
    float blendValue = smoothstep(c_thresholdSensitivity, c_thresholdSensitivity + c_smoothing, distance(vec2(Cr, Cb), vec2(maskCr, maskCb)));
    
    float a = blendValue;
    _surface.transparent.a = a;
    """
    
    shaderModifiers = [ .surface: surfaceShader ]
    

    для установки униформы используйте setValue(:forKey:) метод.

    let vector = SCNVector3(x: 0, y: 1, z: 0) // represents float RGB components
    setValue(vector, forKey: "c_colorToReplace")
    setValue(0.3 as Float, forKey: "c_smoothing")
    setValue(0.1 as Float, forKey: "c_thresholdSensitivity")
    

    на as Float часть важна, в противном случае Swift собирается привести значение как Double и шейдер не сможет его использовать.

    но чтобы получить точную маскировку от этого, вам придется действительно возиться с c_smoothing и c_thresholdSensitivity униформа. В моем примере проекта у меня получилось немного зеленого обода вокруг формы, но, возможно, я просто не использовал правильные значения.

плюсы:

  • требуется только одно видео
  • простая установка

плюсы:

  • возможные артефакты (зеленый обод вокруг границы)