LoginSignup
3
2

More than 5 years have passed since last update.

SwiftでMetalを使用して三角形の色を変更しよう

Last updated at Posted at 2018-11-26

Metalを使用して、シェーダーを使って、三角形の色を後から変更する覚書
「SwiftでMetalを使用して三角形を描画しよう」を参照
https://qiita.com/sanoh/items/98df8d580edaa99c32c9

■Step1:シェーダー変更

まずはShaderType.h を追加します。
スクリーンショット 2018-11-26 19.58.30.png

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」を設定します。
スクリーンショット 2018-11-26 19.59.22.png

■Setp4:実行

iOsは実機で実行してくださいMacの場合は以下のようになります。
スクリーンショット 2018-11-26 19.56.26.png

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2