뷰 컨트롤러
스토리보드에 MTKView 추가하고, mtk view 의 기본적인 설정을 한다.
delegate는 아래 정의할 renderer로 연결
class ViewController: UIViewController {
@IBOutlet weak var mtkView: MTKView!
var renderer: Renderer = .init()
override func viewDidLoad() {
super.viewDidLoad()
self.mtkView.enableSetNeedsDisplay = true
self.mtkView.device = self.renderer.device
self.mtkView.delegate = self.renderer
}
}
정점
이미지만을 표시하기 위해 4각형 정의
let kImagePlaneVertex:[Float] = [
-1.0, -1.0, 0.0, 1.0,
1.0, -1.0, 1.0, 1.0,
-1.0, 1.0, 0.0, 0.0,
1.0, 1.0, 1.0, 0.0,
]
렌더러
mtkview의 delegate
draw 메쏘드는 MTKView에서 호출해 주며, 이때 렌더링을 수행한다.
만약 매번 업데이트되어야 하는 경우 CADisplayLink 등으로 렌더링 루프를 생성해 사용한다.
extension Renderer:MTKViewDelegate {
func draw(in view: MTKView) {
self.render(view: view)
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize ) {
}
}
class Renderer: NSObject {
var device:MTLDevice!
var commandQueue: MTLCommandQueue!
var imageVertexBuffer: MTLBuffer!
var imagePipelineState: MTLRenderPipelineState!
var imageDepthState:MTLDepthStencilState!
var imageVertexFunction: MTLFunction!
var imageFragmentFunction: MTLFunction!
override init() {
super.init()
self.device = MTLCreateSystemDefaultDevice()
initMetal()
}
.
.
.
.
.
}
초기화
func initMetal() {
guard let defaultLibrary = try? self.device.makeDefaultLibrary(bundle: Bundle(for: Renderer.self)) else {
print("[Renderer.initMetal] init error")
return
}
imageVertexFunction = defaultLibrary.makeFunction(name: "imageVertexFunction")
imageFragmentFunction = defaultLibrary.makeFunction(name: "imageFragmentFunction")
self.commandQueue = self.device.makeCommandQueue()
let size = kImagePlaneVertexData.count * MemoryLayout<Float>.size
imageVertexBuffer = self.device.makeBuffer(bytes: kImagePlaneVertexData, length: size)
imageVertexBuffer.label = "ImageVertexBuffer"
let imageVertexDescriptor = MTLVertexDescriptor()
imageVertexDescriptor.attributes[0].format = .float2
imageVertexDescriptor.attributes[0].offset = 0
imageVertexDescriptor.attributes[0].bufferIndex = 0
imageVertexDescriptor.attributes[1].format = .float2
imageVertexDescriptor.attributes[1].offset = 8
imageVertexDescriptor.attributes[1].bufferIndex = 0
imageVertexDescriptor.layouts[0].stride = 16
imageVertexDescriptor.layouts[0].stepRate = 1
imageVertexDescriptor.layouts[0].stepFunction = .perVertex
let imagePipelineDescriptor = MTLRenderPipelineDescriptor()
imagePipelineDescriptor.label = "ImageRenderPipeline"
imagePipelineDescriptor.sampleCount = 1
imagePipelineDescriptor.vertexFunction = imageVertexFunction
imagePipelineDescriptor.fragmentFunction = imageFragmentFunction
imagePipelineDescriptor.vertexDescriptor = imageVertexDescriptor
imagePipelineDescriptor.depthAttachmentPixelFormat = .depth32Float
imagePipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
do {
try self.imagePipelineState = self.device.makeRenderPipelineState(descriptor: imagePipelineDescriptor)
} catch let error {
print("error=\(error.localizedDescription)")
}
// depth state
let depthDescriptor = MTLDepthStencilDescriptor()
depthDescriptor.depthCompareFunction = .lessEqual
depthDescriptor.isDepthWriteEnabled = true
self.imageDepthState = self.device.makeDepthStencilState(descriptor: depthDescriptor)
}
렌더링
func render(view:MTKView) {
print("render")
guard let renderPass = view.currentRenderPassDescriptor else { return }
guard let drawable = view.currentDrawable else { return }
guard let commandBuffer = self.commandQueue.makeCommandBuffer() else { return }
commandBuffer.label = "RenderCommand"
guard let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPass) else {
return
}
renderEncoder.label = "RenderEncoder"
renderEncoder.setCullMode(.front)
renderEncoder.setRenderPipelineState(self.imagePipelineState)
renderEncoder.setDepthStencilState(self.imageDepthState)
renderEncoder.setVertexBuffer(self.imageVertexBuffer, offset: 0, index: 0)
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
renderEncoder.endEncoding()
commandBuffer.present(drawable)
commandBuffer.commit()
}
쉐이더
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
typedef struct {
float3 position [[attribute(0)]];
float2 texCoord [[attribute(1)]];
} ImageVertex;
typedef struct {
float4 position [[position]];
float2 texCoord;
} ImageOut;
vertex ImageOut imageVertexFunction( ImageVertex in [[stage_in]]) {
ImageOut out;
float4 position = float4(in.position, 1.0);
out.position = position;
out.texCoord = in.texCoord;
return out;
}
fragment float4 imageFragmentFunction(ImageOut in [[stage_in]] ) {
return float4( 1.0, 0.0, 0.0, 1.0);
}
'프로그래밍 > iOS,macOS' 카테고리의 다른 글
[Metal] 이미지 렌더링~ 가우시안 블러~ Kernel 쉐이더 (0) | 2021.02.07 |
---|---|
[Metal] 이미지 렌더링~ 가우시안 블러 (0) | 2021.02.07 |
[Metal] 이미지 렌더링~ 여러 텍스처 합치기 (0) | 2021.02.06 |
[Metal] 이미지 렌더링~ 텍스처에 렌더링 (0) | 2021.02.05 |
[Metal] 이미지 렌더링~ 텍스처 표시 (0) | 2021.02.03 |
[Metal] AR 얼굴인식 및 obj 렌더링 (0) | 2021.01.16 |
[Metal] Render to Texture (0) | 2020.12.27 |
UIButton selected + highlighted image (0) | 2020.12.02 |
CaptureSession 카메라, 마이크 (0) | 2020.07.10 |
Codable for Dictionary (0) | 2020.06.10 |