본문 바로가기
Metal

블룸 효과 만들기 with Metal

by ARpple 2024. 3. 11.

커널

 

Kernel (image processing) - Wikipedia

From Wikipedia, the free encyclopedia Matrix used in image processing to alter an image In image processing, a kernel, convolution matrix, or mask is a small matrix used for blurring, sharpening, embossing, edge detection, and more. This is accomplished by

en.wikipedia.org

  1. Box Blur → 흐리게 만들어준다.
  2. Gaussian Blur → 흐리게 만들어준다.
Weights: [0.0545f, 0.2442f, 0.4026f, 0.2442f, 0.0545f]
  1. Bloom Filter → 명암 대비를 더 크게 만들어준다.
    • 원본 이미지 + 블러 이미
    • Relative Luminance Y = 0.2126R + 0.7152G + 0.0722*B

Convolution

(0,0) 좌표의 픽셀을 Convolution하는 방법

  • 음수의 영역의 픽셀을 가장 인접한 픽셀의 데이터로 대입한다.

Separable Convolution

1차원 Convolution을 가로 방향으로 진행 후, 세로 방향으로 진행

블룸 효과를 준 레몬 침착맨

원본 이미지

 

 

침착맨 레몬의 효능 장패드 ㅣ 마플샵

침착맨 레몬의 효능 장패드, 레몬의 효능 장패드 22,000원

marpple.shop

Github Repository

 

GitHub - TaeYoon17/MetalGraphics: 그래픽스 기술을 Metal API로 구현하기

그래픽스 기술을 Metal API로 구현하기. Contribute to TaeYoon17/MetalGraphics development by creating an account on GitHub.

github.com

핵심 개념

  • SIMD 연산
  • CIImage
    • CIImage로 이미지 Pixel들에 접근해서 Vertex 배열로 변환함
  • CGContext
    • data , width, height, bitsPerComponent, bytesPerRow
그래픽 컨텍스트에는 대상 페이지가 애플리케이션의 창, 비트맵 이미지, PDF 문서 또는 프린터인지 여부에 관계없이 페이지의 페인트를 대상에 렌더링하는 데 필요한 그리기 매개변수와 모든 디바이스별 정보가 포함되어 있습니다.
  • CGColorSpaceCreateDeviceRGB
기기에 맞는 RGB 값을 생성하는 메서드

사용자 이미지를 Position,RGB 정보로 담긴 Vertex로 만들고 Metal로 그리기

  1. 사용자 이미지를 CIImage로 가져오기
  2. CIImage로 CGImage를 생성 후, CFData로 변환
  3. CFData의 포인터 주소 배열 가져오기
    ⇒ 이미지가 2차원에서 1차원 배열로 변환됨
  1. Metal Shader에서 사용할 Vertex 배열로 변환하기
    1. 원본 이미지 좌표게에서 메탈 좌표계로 변환
    2. 원본 색상 값에서 메탈 색상 값으로 변환
  2. 원본 이미지에서 추출한 Vertex 배열 값에 Bloom 필터 효과 주기
  3. Metal Shader로 그리기

메탈 좌표계와 이미지 방향 맞추기

 

CIImage의 좌표계는 다행히 Metal과 방향이 같다. 좌표 변환을 위한 Reverse 작업은 없음

핵심 코드

let pointer = CFDataGetBytePtr(data) // CIImage를 CFData의 포인터로 저장함

// 이미지(CIImage) Coordinates to Metal Coordinates

for y in 0..<height {

        for x in 0..<width {

            let pixelInfo = y * bytesPerRow + x * bytesPerPixel

            let red = Float(pointer![pixelInfo]) / 255.0

            let green = Float(pointer![pixelInfo + 1]) / 255.0

            let blue = Float(pointer![pixelInfo + 2]) / 255.0

            // 원본 이미지의 Row 배열이 [1,2,3,4,5]라면 현제 Vertex에 담긴 배열은 [5,4,3,2,1]이 된다.

            // 메탈은 하나의 Row를 동시에 읽기 때문에 y좌표만 반대로 바꾸어서(Reverse) 2차원 배열에 담는다.

            vetecies.append(Vertex(position: .init(x: 2*(Float(x) / Float(width) - 0.5),

                                                   y: -2*(Float(y) / Float(height) - 0.5)),

                                   color: [red,green,blue,1]))

        }

    }

Bloom 효과 만들기

  • inout을 이용해서 Vertex 값을 참조해서 연산하기
  • simd 메서드를 통해 빠르게 연산시키기
  • Bloom 효과 = 원본을 GaussianBlur 처리한 이미지 + 원본에서 임계값 이하는 Black 처리한 이미지

문제점

  • 시간을 계산하니 SIMD를 사용하지만, CPU로 모든 Bloom 그래픽 처리를 완료한 후 Metal로 그리는 로직으로 약 10초!!를 연산을 기다려야한다.
let startTime = DispatchTime.now()

    makeBloom(vertices: &vetecies, width: width, height: height, th: 0.33,weight: 0.2)

    let endTime = DispatchTime.now()

    let nanoTime = endTime.uptimeNanoseconds - startTime.uptimeNanoseconds

    let executionTime = Double(nanoTime) / 1_000_000_000 // 초 단위로 변환

    print("Execution time: \(executionTime) seconds")

코드

https://github.com/TaeYoon17/MetalGraphics/tree/main/Bloom

'Metal' 카테고리의 다른 글

무게 중심 좌표계, Barycentric coordinates  (0) 2024.04.30
Perspective Projection & Phong Shading  (0) 2024.04.22
RayTracing으로 삼각형 그리기  (0) 2024.04.08
Raytracing으로 구 그리기  (0) 2024.03.31

댓글