멋진 입자 효과 합성 사진
프로젝트 중의 일부 특수효과를 실현하기 위해 최근에 코드를 벗겼는데 무심결에 이 특수효과를 발견했는데 효과가 매우 멋있다고 느꼈다. 원래 실현하기가 매우 어려울 것 같았지만 원본 코드를 본 후에 실현하는 방식이 매우 간단하다는 것을 발견했다.주요 절차는 먼저 그림을 픽셀로 분해한 다음에 CADisplayLink를 이용하여 화면과 동시 리셋 애니메이션을 하는 것이다.원본 코드는
OC인데 Swfit판을 사용해야 하기 때문에 전체 코드를 Swift3.0로 다시 한 번 썼습니다.Swift의 몇 가지 원인으로 중간에 문제가 생겼기 때문에 여기서 다시 한 번 기록하면 옛 것을 배우고 새로운 것을 알 수 있겠는가!이것은 최종 효과도, 입자의 발사 속도, 시간, 기점 등을 모두 제어할 수 있는 것이다.
문제.
문제는
UnsafeMutableRawPointer의 데이터를 어떻게 해석해야 하는가이다. 해결 방법은 다음과 같다.let rawData: UnsafeMutableRawPointer = calloc(imageH*imageW*bytesPerPixel, MemoryLayout.size(ofValue: CChar()))
.
.
.
let bufferData = UnsafeRawBufferPointer(start: rawData, count: imageH*imageW*bytesPerPixel)
UnsafeRawBufferPointer를 통해 데이터를 분석하면count 다음에 데이터가 차지하는 바이트 크기입니다코드 예
//
// BZEmitterLayer.swift
// BZEmitter
//
// Copyright © 2017 SSBun. All rights reserved.
//
import Foundation
import QuartzCore
import UIKit
struct BZParticle {
var color: UIColor
var point: CGPoint
var customColor: UIColor? {
set {
if let value = newValue {
color = value
}
}
get {
return color
}
}
var randomPointRange: CGFloat? {
set {
let value = newValue ?? 0
if value != 0 {
point.x = point.x - value + CGFloat(arc4random_uniform(UInt32(value) * 2))
point.y = point.y - value + CGFloat(arc4random_uniform(UInt32(value) * 2))
}
}
get {
return 0
}
}
let delayTime: UInt32 = arc4random_uniform(30)
let delayDuration: UInt32 = arc4random_uniform(10)
}
protocol BZEmitterLayerDelegate {
func emitterLayerEndAnimation()
}
class BZEmitterLayer: CALayer {
public var beginPoint: CGPoint = .zero//
public var ignoredBlack: Bool = false//
public var ignoredWhite: Bool = false//
public var customColor: UIColor? // ,
public var randomPointRange: CGFloat = 0// 0
public var maxParticleCount: UInt32 = 0 //
public var image: UIImage? { //
didSet {
if let image = image {
particleArray = self.getRGBAs(from: image)
}
}
}
public var emitterDelegate: BZEmitterLayerDelegate?
private var animationTime: CGFloat = 0
private var animationDuration: CGFloat = 2
private var displayLink:CADisplayLink?
private var particleArray:[BZParticle] = []
override init() {
super.init()
self.masksToBounds = false
displayLink = CADisplayLink(target: self, selector: #selector(BZEmitterLayer.emitterAnimation))
displayLink?.add(to: .current, forMode: .commonModes)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func emitterAnimation() {
self.setNeedsDisplay()
animationTime += 0.6
}
override func draw(in ctx: CGContext) {
var count = 0
for particle in particleArray {
if CGFloat(particle.delayTime) > animationTime {
continue
}
var curTime = animationTime - CGFloat(particle.delayTime)
if curTime > animationDuration + CGFloat(particle.delayDuration) {
curTime = animationDuration + CGFloat(particle.delayDuration)
count += 1
}
let curX = self.easeInOutQuad(curTime, beginPoint.x, particle.point.x + self.bounds.size.width/2 - CGFloat(image!.cgImage!.width/2), animationDuration + CGFloat(particle.delayDuration))
let curY = self.easeInOutQuad(curTime, beginPoint.y, particle.point.y + self.bounds.size.height/2 - CGFloat(image!.cgImage!.height/2), animationDuration + CGFloat(particle.delayDuration))
ctx.addRect(CGRect(x:curX, y:curY, width:1, height:1))
let components = particle.color.cgColor.components!
ctx.setFillColor(red: components[0], green: components[1], blue: components[2], alpha: components[3])
ctx.fillPath()
}
if (count == particleArray.count) {
self.reset()
self.emitterDelegate?.emitterLayerEndAnimation()
}
}
func easeInOutQuad(_ time: CGFloat, _ begin: CGFloat, _ end: CGFloat, _ duration: CGFloat) -> CGFloat {
let coverDistance = end - begin
var newTime = time / (duration/2)
if newTime < 1 {
return coverDistance/2.0 * pow(newTime, 2) + begin
}
newTime -= 1
return -coverDistance/2.0 * (newTime * (newTime - 2) - 1) + begin
}
func getRGBAs(from image: UIImage) -> [BZParticle] {
let imageRef = image.cgImage!
let imageW = imageRef.width
let imageH = imageRef.height
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bytesPerPixel = 4 // 4
let bytesPerRow = bytesPerPixel * imageW
let rawData: UnsafeMutableRawPointer = calloc(imageH*imageW*bytesPerPixel, MemoryLayout.size(ofValue: CChar()))
let bitsPerComponent = 8
let context = CGContext(data: rawData, width: imageW, height: imageH, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageByteOrderInfo.order32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue)
context?.draw(imageRef, in: CGRect(x: 0, y: 0, width: imageW, height: imageH))
let addY = maxParticleCount == 0 ? 1 : imageH / Int(maxParticleCount)
let addX = maxParticleCount == 0 ? 1 : imageW / Int(maxParticleCount)
var result = [BZParticle]()
let bufferData = UnsafeRawBufferPointer(start: rawData, count: imageH*imageW*bytesPerPixel)
for y in stride(from: 0, to: imageH, by: addY) {
for x in stride(from: 0, to: imageW, by: addX) {
let byteIndex = bytesPerRow*y + bytesPerPixel*x
let red = CGFloat(bufferData[byteIndex]) / 255.0
let green = CGFloat(bufferData[byteIndex + 1]) / 255.0
let blue = CGFloat(bufferData[byteIndex + 2]) / 255.0
let alpha = CGFloat(bufferData[byteIndex + 3]) / 255.0
if alpha == 0 || (ignoredWhite && (red+green+blue == 3)) || (ignoredBlack && (red+green+blue == 0)) {
continue
}
let color = UIColor(red: red, green: green, blue: blue, alpha: alpha)
let point = CGPoint(x: x, y: y)
var particle = BZParticle(color: color, point: point)
if let custom = customColor {
particle.customColor = custom
}
if randomPointRange > 0 {
particle.randomPointRange = randomPointRange
}
result.append(particle)
}
}
free(rawData)
return result
}
func pause() {
displayLink?.isPaused = true
}
func resume() {
displayLink?.isPaused = false
}
func reset() {
displayLink?.invalidate()
displayLink = nil
animationTime = 0
}
func restart() {
self.reset()
displayLink = CADisplayLink(target: self, selector: #selector(BZEmitterLayer.emitterAnimation))
displayLink?.add(to: .current, forMode: .commonModes)
}
}
데모 주소
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.