Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away