Metalを使用して、シェーダーを使って、三角形の色を後から変更する覚書
「SwiftでMetalを使用して三角形を描画しよう」を参照
https://qiita.com/sanoh/items/98df8d580edaa99c32c9
■Step1:シェーダー変更
ShaderType.h
#ifndef ShaderType_h
#define ShaderType_h
#ifdef __METAL_VERSION__
#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
#define NSInteger metal::int32_t
#else
#import <Foundation/Foundation.h>
#endif
#include <simd/simd.h>
typedef NS_ENUM(NSInteger, BufferIndex)
{
BufferIndexPositions = 0,
BufferIndexUniforms = 1
};
typedef struct
{
float color[3];
} Uniforms;
#endif /* ShaderType_h */
Shader.Metal を編集
Shader.metal
#include <metal_stdlib>
#import "ShaderType.h"
using namespace metal;
vertex float4 myVertexShader(const device packed_float3* vertex_array [[ buffer(BufferIndexPositions) ]],
unsigned int vid [[ vertex_id ]]) {
return float4(vertex_array[vid], 1.0);
}
fragment half4 myFragmentShader(constant Uniforms & uniforms [[ buffer(BufferIndexUniforms) ]]) {
return half4(uniforms.color[0], uniforms.color[1], uniforms.color[2] , 1.0);
}
■Step2:MetalRenderer.swiftを編集します。
MetalRenderer.swift
import Metal
import MetalKit
import simd
let alignedUniformsSize = (MemoryLayout<Uniforms>.size & ~0xFF) + 0x100
let maxBuffersInFlight = 3
class MetalRenderer: NSObject, MTKViewDelegate {
let _device: MTLDevice
let _command_queue: MTLCommandQueue
let _in_flight_semaphore = DispatchSemaphore(value: maxBuffersInFlight)
let _buffer_position : MTLBuffer!
let _render_pipeline_state: MTLRenderPipelineState
let _dynamic_uniform_buffer:MTLBuffer
var _uniform_buffer_index = 0
init?(metalKitView: MTKView) {
let vertices_array:[Float] =
[
0.0, 1.0, 0.0,
-1.0, -1.0, 0.0,
1.0, -1.0, 0.0
]
_device = metalKitView.device!
_command_queue = _device.makeCommandQueue()!
metalKitView.colorPixelFormat = MTLPixelFormat.bgra8Unorm
let size = vertices_array.count * MemoryLayout<Float>.size
_buffer_position = _device.makeBuffer(bytes: vertices_array, length: size)
guard let library = _device.makeDefaultLibrary() else { fatalError() }
let descriptor = MTLRenderPipelineDescriptor()
descriptor.vertexFunction=library.makeFunction(name: "myVertexShader")
descriptor.fragmentFunction=library.makeFunction(name: "myFragmentShader")
descriptor.colorAttachments[0].pixelFormat = metalKitView.colorPixelFormat
_render_pipeline_state = try! _device.makeRenderPipelineState(descriptor: descriptor)
let uniform_buffer_size = alignedUniformsSize * maxBuffersInFlight
_dynamic_uniform_buffer = _device.makeBuffer(length: uniform_buffer_size, options:[MTLResourceOptions.storageModeShared])!
super.init()
}
func draw(in view: MTKView) {
_ = _in_flight_semaphore.wait(timeout: DispatchTime.distantFuture)
if let command_buffer = _command_queue.makeCommandBuffer() {
let semaphore = _in_flight_semaphore
command_buffer.addCompletedHandler { (_ command_buffer)-> Swift.Void in
semaphore.signal()
}
let render_pass_descriptor = view.currentRenderPassDescriptor
render_pass_descriptor?.colorAttachments[0].loadAction=MTLLoadAction.clear
render_pass_descriptor?.colorAttachments[0].storeAction=MTLStoreAction.store
render_pass_descriptor?.colorAttachments[0].clearColor=MTLClearColorMake(1.0, 0.0, 0.0, 1.0)
if let render_pass_descriptor = render_pass_descriptor {
_uniform_buffer_index = (_uniform_buffer_index + 1) % maxBuffersInFlight
let uniform_buffer_offset = alignedUniformsSize * _uniform_buffer_index
let uniforms = UnsafeMutableRawPointer(_dynamic_uniform_buffer.contents() + uniform_buffer_offset).bindMemory(to:Uniforms.self, capacity:1)
uniforms[0].color.0 = 0.0 // R 色指定
uniforms[0].color.1 = 0.0 // G
uniforms[0].color.2 = 1.0 // B
if let render_encoder = command_buffer.makeRenderCommandEncoder(descriptor: render_pass_descriptor) {
render_encoder.setFragmentBuffer(_dynamic_uniform_buffer, offset:uniform_buffer_offset, index: BufferIndex.uniforms.rawValue)
render_encoder.setRenderPipelineState(_render_pipeline_state)
render_encoder.setVertexBuffer(_buffer_position, offset: 0, index: BufferIndex.positions.rawValue)
render_encoder.drawPrimitives(type: MTLPrimitiveType.triangle, vertexStart: 0, vertexCount: 3)
render_encoder.endEncoding()
if let drawable = view.currentDrawable {
command_buffer.present(drawable)
}
}
}
command_buffer.commit()
}
}
func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
}
}
■Setp3:ヘッダーファイルをSwiftから参照できるようにする。
Swiftからは直接ヘッダーファイルを参照できないので、設定します。
「Build Setting」の「Objective-C Bridging Header」に「ShaderType.h」を設定します。