버텍스
let vertexData:[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,
]
버퍼
let size = vertexData.count * MemoryLayout<Float>.size
// var vertexBuffer:MTLBuffer
vertexBuffer = device.makeBuffer(bytes: vertexData, length: size)
vertexBuffer.label = "VertexBuffer"
렌더타겟용 텍스처 생성
렌더링에 사용할 텍스처는 MTKTextureLoader 등으로 별로 생성
let texDescriptor = MTLTextureDescriptor()
texDescriptor.textureType = MTLTextureType.type2D
texDescriptor.width = 1024
texDescriptor.height = 1024
texDescriptor.pixelFormat = .bgra8Unorm
texDescriptor.storageMode = .shared
texDescriptor.usage = [.renderTarget, .shaderRead]
// var renderTargetTexture: MTLTexture
renderTargetTexture = device.makeTexture(descriptor: texDescriptor)
// 프레그먼트 쉐이더에 사용할 텍스처는 필요한 형태로 준비(아래는 리소스에서 읽어오는 예)
let textureLoader = MTKTextureLoader(device: device)
renderTexture = loader.newTexture("image",
scaleFactor: 1.0,
bundle: nil,
options[:] )
텍스처 타겟 렌더패스
let clearColor = MTLClearColor(red:0.0, green:0.0,blue:0.0, alpha:1.0)
renderPassDescriptor = MTLRenderPassDescritor()
renderPassDescriptor.colorAttachments[0].texture = renderTargetTexture
renderPassDescriptor.colorAttachments[0].loadAction = .clear
renderPassDescriptor.colorAttachments[0].clearColor = clearColor
renderPassDescriptor.colorAttachments[0].storeAction = .store
텍스처 타겟 렌더파이프라인
var library = device.makeDefaultLibrary()
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].format = .float2
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[1].format = .float2
vertexDescriptor.attributes[1].offset = 8
vertexDescriptor.attributes[1].bufferIndex = 0
vertexDescriptor.layout[0].stride = 16
vertexDescriptor.layout[0].stepRate = 1
vertexDescriptor.layout[0].stepFunction = .perVertex
let pipelineDescriptor = MTLRenderPipelineDescriptor()
pipelineDescriptor.lable = "Render Pipeline"
pipelineDescriptor.vertexDescriptor = vertexDescriptor
pipelineDescriptor.sampleCount = 1
pipelineDescriptor.vertexFunction = library.makeFunction(name: "vertexFunction")
pipelineDescriptor.fragmentFunction = library.makeFunction(name: "fragmentFunction")
pipelineDescriptor.colorAttachments[0].pixelFormat = renderTargetTexture.colorPixelFormat
do {
// var pipelineState: MTLRenderPipelineState
try pipelineState = device.makeRenderPipelineState(descriptor: pipelineDescriptor)
} catch let error {
}
렌더링
let commandBuffer = commandQueue.makeCommandBuffer()
commandBuffer.label = "MyCommand"
let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
renderEncoder.label = "RenderEncoder"
renderEncoder.setCullMode(.none)
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer, offset:0, index, 0)
renderEncoder.setFragmentTexture( renderTexture, index 0 )
renderEncoder.drawPrimitives(type: .triangleStrip, vertexStart:0, vertexCount: 4)
renderEncoder.endEncoding()
commandBuffer.commit()
쉐이더
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
typedef struct {
float2 position [[attribute(0)]]
float2 texCoord [[attribute(1)]]
} VertexData;
typedef struct {
float4 position [[position]];
float2 texCoord;
} VertexDataOut;
vertex VertexDataOut vertexFunction(VertexData in [[stage_in]]) {
VertexDataOut out;
out.position = float4(in.position, 0.0, 1.0);
out.texCoord = in.texCoord;
return out
}
fragment float4 fragmentFunction(VertexDataOut in [[stage_in]],
texture2d<float> imageTexutre [[texture(0)]]) {
constexpr sampler colorSampler(mip_filter::linear,
mag_filter::linear,
min_filter::linear);
float4 colorSample = imageTexture.sample(colorSampler, in.texCoord);
return colorSample;
}
렌더링 루프
MTKView, SCNView 등 렌더링을 위한 delegate 메쏘드가 호출되면 렌더링을 수행하거나 별도 렌더링을 위한 메쏘드가 없는 상황에서는 직접 렌더링을 위한 루프를 생성해 주어야 한다.
CADisplayLink 를 사용한 렌더링 루프 예
.
.
// CADisplayLink
let displayLink = CADisplayLink( target:self, selector:#selector(displayUpdate))
displayLink.add( to: .main, forMode: .default)
.
.
.
}
// 콜백
@objc func displayUpdate(displaylink: CADisplayLink) {
let cmTime = CMTime(seconds:displaylink.timestamp, preferredTimescale: 1000000)
// 렌더링 호출
}
텍스처를 CVPixelBuffer 로 복사
렌더타겟을 텍스처로 지정한 경우 해당 데이터를 CVPixelBuffer로 복사해 사용할 일들이 있는데, 이때 아래와 같이 생성해둔 pixel buffer로 데이터를 복사해 사용할 수 있다.
var pixelBuffer:CVPixelBuffer?
CVPixelBufferCreate(kCFAllocatorDefault,
texture.width,
texture.height,
kCVPixelFormatType_32BGRA,
nil,
&pixelBuffer)
.
.
CVPixelBufferLockBaseAddress(pixelBuffer, [] )
let pixelBufferBytes = CVPixelBufferGetBaseAddress(pixelBuffer)!
let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
let region = MTLRegionMake2D(0, 0, texture.width, texture.height)
texture.getBytes(pixelBufferBytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel:0)
CVPixelBufferUnlockBaseAddress(pixelBuffer, [])
'프로그래밍 > iOS,macOS' 카테고리의 다른 글
[Metal] 이미지 렌더링~ 여러 텍스처 합치기 (0) | 2021.02.06 |
---|---|
[Metal] 이미지 렌더링~ 텍스처에 렌더링 (0) | 2021.02.05 |
[Metal] 이미지 렌더링~ 텍스처 표시 (0) | 2021.02.03 |
[Metal] 이미지 렌더링~ 사각형 그리기 (0) | 2021.02.03 |
[Metal] AR 얼굴인식 및 obj 렌더링 (0) | 2021.01.16 |
UIButton selected + highlighted image (0) | 2020.12.02 |
CaptureSession 카메라, 마이크 (0) | 2020.07.10 |
Codable for Dictionary (0) | 2020.06.10 |
UITableView , UICollectionView (1) | 2020.04.14 |
[iOS] 키보드를 따라 올라오는 뷰 (0) | 2020.04.08 |