用RealityKit拍照, 并存到相冊

禁止轉(zhuǎn)載

AR應用, 用RealityKit呈現(xiàn), 要實現(xiàn)拍照與錄像的功能, github上搜到有個庫, 但是只支持ARSCNView, 而且感覺這個功能算是比較正常的功能吧, 竟然沒搜到其他實現(xiàn)的, 踩了不少的坑只實現(xiàn)了拍照, 不過效果還算不錯.

此方法只適用于iOS15及之后.


示例.png

開始的思路是獲取到ARFrame的一幀, 然后給存到相冊. 正好ARSessionDelegate的session(_ session: ARSession, didUpdate frame: ARFrame)方法會返回ARFrame, ARFrame包含capturedImage. 結(jié)果這個capturedImage是只有相機的內(nèi)容, 看網(wǎng)上有人說他是拿到相機內(nèi)容后再用metal把虛擬內(nèi)容渲上去, 感覺直接用metal渲的可能還是少數(shù)吧.

然后我想到用ReplayKit去錄屏, 但是用這種方法每次錄屏都會彈出一個系統(tǒng)提示, 就也放棄這個方法了.

一籌莫展之際, 我想起了蘋果官方postProcess的代碼, 后期處理的話應該是相機與虛擬內(nèi)容都渲好的圖像. 試了一下果然是ok的. 流程大概是arView有renderCallbacks, 其中prepareWithDevice的回調(diào)獲取ciContext, 然后postProcess的回調(diào)獲取到CIImage, 再轉(zhuǎn)成UIImage保存到相冊.

在此之前, 先檢查適合當前設備的輸出材質(zhì)的像素格式

@available(iOS 15.0, *)
extension RealityKit.ARView.PostProcessContext {
    // Returns the output texture, ensuring that the pixel format is
    // appropriate for the current device's GPU.
    var compatibleTargetTexture: MTLTexture! {
        if self.device.supportsFamily(.apple2) {
            return targetColorTexture
        } else {
            return targetColorTexture.makeTextureView(pixelFormat: .bgra8Unorm)!
        }
    }
}

注冊prepareWithDevice的回調(diào), 并保存CIContext

arView.renderCallbacks.prepareWithDevice = setupCoreImage
func setupCoreImage(device: MTLDevice) {
    // Create a CIContext and store it in a property.
    self.ciContext = CIContext(mtlDevice: device)

    // Do other expensive tasks, like loading images, here.
}

注冊postProcess的回調(diào), 獲取需要的UIImage, 并渲染. 這里是與官方后期處理的代碼不同的地方, 畢竟我只是想要一幀而不是想要后期處理. 所以如果對比的話會發(fā)現(xiàn)我刪除了濾鏡的部分.

arView.renderCallbacks.postProcess = postProcessWithCoreImage
func postProcessWithCoreImage(context: ARView.PostProcessContext) {

    // Convert the frame buffer from a Metal texture to a CIImage, and 
    // set the CIImage as the filter's input image.
    guard let input = CIImage(mtlTexture: context.sourceColorTexture) else {
        fatalError("Unable to create a CIImage from sourceColorTexture.")
    }

    // 確保只拿一幀
    if cameraState == .needCapture {
        cameraState = .captured
            
        DispatchQueue.main.async { [weak self] in
            // 因為渲染時的坐標系(左下0,0)與UIKit的坐標系(左上0,0)不同, 所以這里需要縱向鏡像一下
            let upsideDown = input.oriented(.downMirrored)
            let image = UIImage(ciImage: upsideDown)
            // 獲取到我們需要的UIImage
            self?.tempImage = image
        }
    }
    // 下面這部分代碼的作用是把內(nèi)容渲染到arView上
    // Create a render destination and render the filter to the context's command buffer.
    let destination = CIRenderDestination(mtlTexture: context.compatibleTargetTexture,
                                          commandBuffer: context.commandBuffer)
    destination.isFlipped = false
    _ = try? self.ciContext.startTask(toRender: input, to: destination)
}

最后是保存到相冊

    func saveImage() {
        DispatchQueue.main.async { [weak self] in
            self?.saveJpg((self?.tempImage)!)
            let path = self?.documentDirectoryPath()?.appendingPathComponent("temp.jpg")
            let image = UIImage.init(contentsOfFile: path!.path)!
            UIImageWriteToSavedPhotosAlbum(image, self, #selector(self?.saveImageComplete), nil)
        }
    }

    func saveJpg(_ image: UIImage) {
        if let jpgData = image.jpegData(compressionQuality: 0.5),
            let path = documentDirectoryPath()?.appendingPathComponent("temp.jpg") {
            try? jpgData.write(to: path)
        }
    }
    
    func documentDirectoryPath() -> URL? {
        let path = FileManager.default.urls(for: .documentDirectory,
                                            in: .userDomainMask)
        return path.first
    }

    @objc func saveImageComplete(image:UIImage, didFinishSavingWithError error:NSError?, contextInfo:AnyObject) {
        if error == nil {
            showAlert(title: "圖片已存至相冊")
        } else {
            showAlert(title: "保存到相冊失敗")
        }
    }

保存相冊這步看網(wǎng)上都是只有UIImageWriteToSavedPhotosAlbum這一步, 看方法描述的話確實感覺這個方法傳一個UIImage就ok了, 結(jié)果是如果不把UIImage先給存下來的話, 會遇到下面未知錯誤, 賊坑

Error Domain=ALAssetsLibraryErrorDomain Code=-1 "未知錯誤" UserInfo={NSLocalizedDescription=未知錯誤, NSUnderlyingError=0x2832cefd0 {Error Domain=PHPhotosErrorDomain Code=3303 "(null)"}}

理論上postProcess那里可以拿到每秒60幀所以實現(xiàn)錄像的話應該也可以, 但是我還沒研究出來具體怎么實現(xiàn)所以有大佬的話可以指點一下.

我現(xiàn)在是全職做iOS AR開發(fā), 感覺坑是真的多, 而且網(wǎng)上能搜到的內(nèi)容比較少, 如果有跟我一樣感受的不妨加個好友我們可以互相避坑.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載墓猎,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者第煮。
  • 序言:七十年代末逼争,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子箭昵,更是在濱河造成了極大的恐慌寝杖,老刑警劉巖扰才,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件雄右,死亡現(xiàn)場離奇詭異,居然都是意外死亡巷送,警方通過查閱死者的電腦和手機驶忌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笑跛,“玉大人付魔,你說我怎么就攤上這事》甚澹” “怎么了几苍?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵,是天一觀的道長陈哑。 經(jīng)常有香客問我妻坝,道長伸眶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任刽宪,我火速辦了婚禮厘贼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘圣拄。我一直安慰自己嘴秸,他們只是感情好,可當我...
    茶點故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布庇谆。 她就那樣靜靜地躺著岳掐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪饭耳。 梳的紋絲不亂的頭發(fā)上串述,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機與錄音寞肖,去河邊找鬼剖煌。 笑死,一個胖子當著我的面吹牛逝淹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播桶唐,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼栅葡,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了尤泽?” 一聲冷哼從身側(cè)響起欣簇,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎坯约,沒想到半個月后熊咽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡闹丐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年横殴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卿拴。...
    茶點故事閱讀 40,872評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡衫仑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出堕花,到底是詐尸還是另有隱情文狱,我是刑警寧澤,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布缘挽,位于F島的核電站瞄崇,受9級特大地震影響呻粹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜苏研,卻給世界環(huán)境...
    茶點故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一等浊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楣富,春花似錦凿掂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至塘安,卻和暖如春糠涛,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兼犯。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工忍捡, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人切黔。 一個月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓砸脊,卻偏偏與公主長得像,于是被迫代替她去往敵國和親纬霞。 傳聞我的和親對象是個殘疾皇子凌埂,可洞房花燭夜當晚...
    茶點故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容