iOS/라이브러리

[iOS, Metal] Metal에서 Image를 MTKView에 올리는 방법

검은참깨두유vm 2023. 3. 21. 00:00
반응형

이 글에서는 Metal에서 이미지를 MTKView에 올리는 방법에 대해 알아보겠습니다.

 

1. 이미지 로드하기

Metal에서 이미지를 사용하기 위해서는 이미지를 로드해야 합니다.

guard let image = UIImage(named: "{이미지 파일 이름}") else {
    fatalError("이미지를 로드하는 데 실패했습니다.")
}

 

2. Texture 생성하기

이미지를 로드했다면, 이제 해당 이미지를 Metal에서 사용할 수 있는 Texture로 변환해야 합니다.

guard let texture = try? MTLTextureLoader(device: device).newTexture(cgImage: image.cgImage!, options: nil) else {
    fatalError("Texture를 생성하는 데 실패했습니다.")
}

 

 

3. Pipeline 생성하기

Pipeline은 GPU에서 동작하는 코드를 구성하는데 필요한 정보를 담고 있는 객체입니다. 이번 예제에서는 Vertex와 Fragment Shader를 작성하여 Pipeline을 생성합니다.

 

3-1. Vertex Shader 작성하기

Vertex Shader는 입력 데이터를 변환하는 역할을 합니다. 이번 예제에서는 2D 이미지를 출력하기 때문에, 2D 좌표값을 입력으로 받아 3D 좌표값으로 변환하는 작업을 수행하도록 작성합니다.

 

#include <metal_stdlib>

using namespace metal;

struct VertexIn {
    float2 position;
};

struct VertexOut {
    float4 position [[position]];
    float2 textureCoordinate;
};

vertex VertexOut vertexShader(VertexIn vertexIn [[stage_in]]) {
    VertexOut vertexOut;
    vertexOut.position = float4(vertexIn.position, 0.0, 1.0);
    vertexOut.textureCoordinate = float2((vertexIn.position.x + 1.0) / 2.0, (1.0 - vertexIn.position.y) / 2.0);

    return vertexOut;
}

 

3-2. Fragment Shader 작성하기

Fragment Shader는 Vertex Shader에서 출력된 값을 이용하여 색상 정보를 생성합니다. 이번 예제에서는 Texture를 이용하여 색상 정보를 생성하도록 작성합니다.

 

#include <metal_stdlib>

using namespace metal;

struct FragmentIn {
    float2 textureCoordinate;
};

fragment half4 fragmentShader(FragmentIn fragmentIn [[stage_in]],
                              texture2d<half> texture [[texture(0)]]) {
    constexpr sampler textureSampler(mag_filter::linear, min_filter::linear);
    return half4(texture.sample(textureSampler, fragmentIn.textureCoordinate).rgb, 1.0);
}

 

3-3. Pipeline 생성하기

Vertex Shader와 Fragment Shader를 이용하여 Pipeline을 생성합니다.

 

let library = try! device.makeLibrary(source: "{위에서 작성한 MSL 코드}", options: nil)
let vertexFunction = library.makeFunction(name: "vertexShader")
let fragmentFunction = library.makeFunction(name: "fragmentShader")

let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.vertexFunction = vertexFunction
pipelineDescriptor.fragmentFunction = fragmentFunction
pipelineDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat

let pipelineState = try! device.makeRenderPipelineState(descriptor: pipelineDescriptor)

 

4. MTKView에 Texture 출력하기

Texture를 생성하고, Pipeline을 생성했다면, 이제 해당 Texture를 MTKView에 출력할 수 있습니다.

 

mtkView.drawableSize = view.bounds.size

guard let commandBuffer = commandQueue.makeCommandBuffer(),
      let drawable = mtkView.currentDrawable else {
    fatalError("MTKView 설정에 실패했습니다.")
}

let renderPassDescriptor = mtkView.currentRenderPassDescriptor
renderPassDescriptor?.colorAttachments[0].texture = drawable.texture

let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor!)
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
renderEncoder.setFragmentTexture(texture, index: 0)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
renderEncoder.endEncoding()

commandBuffer.present(drawable)
commandBuffer.commit()

 

위 코드에서 renderEncoder.drawPrimitives() 메소드에서는 출력할 도형의 형태를 지정합니다. 위 코드에서는 삼각형 스트립을 출력하는 예제입니다.

이제 위 코드를 사용하여 Metal에서 이미지를 MTKView에 출력할 수 있습니다.

반응형