LoginSignup
3
2

More than 5 years have passed since last update.

SwiftでMetalを使用してテクスチャを表示しよう

Posted at

Metalを使用して、テクスチャ表示する覚書
「SwiftでMetalを使用して三角形を描画しよう」を参照
https://qiita.com/sanoh/items/98df8d580edaa99c32c9

■Step1:シェーダー変更

まずはShaderType.h を追加します。
スクリーンショット 2018-11-30 14.22.14.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)
{
    BufferIndexMeshPositions = 0
};

typedef NS_ENUM(NSInteger, TextureIndex)
{
    TextureIndexColor    = 0,
};

#endif /* ShaderType_h */

Shader.Metal を編集

Shader.Metal
#include <metal_stdlib>
#include <simd/simd.h>
#import "ShaderType.h"

using namespace metal;

struct Vertex {
    float2 position;
    float2 texCoord;
};

struct ColorInOut{
    float4  position [[position]];
    float2  texCoord;
};

vertex ColorInOut myVertexShader(constant Vertex *vertices [[buffer(BufferIndexMeshPositions)]],
                                 uint vid [[vertex_id]]) {
    ColorInOut  out;

    out.position    = float4(vertices[vid].position, 0.0, 1.0);
    out.texCoord    = vertices[vid].texCoord;
    return out;
}

fragment half4 myFragmentShader(ColorInOut vert [[stage_in]],
                                texture2d<half> colorMap     [[ texture(TextureIndexColor) ]]) {
    constexpr sampler defaultSampler;
    half4 imageColor = colorMap.sample(defaultSampler, vert.texCoord.xy);
    return imageColor;
}

■Steop2:MetalRenderer.swiftを編集します。

MetalRenderer.swift
import MetalKit

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 _color_image: MTLTexture    //<新規>

    init?(metalKitView: MTKView) {
        //頂点データ<更新>
        let vertices_array:[Float] =
            [
                +0.0, +1.0, 0.0, 0.0,
                -1.0, -1.0, 1.0, 0.0,
                +1.0, -1.0, 1.0, 1.0,
            ]
        _device = metalKitView.device!
        metalKitView.colorPixelFormat = MTLPixelFormat.bgra8Unorm
        _command_queue = _device.makeCommandQueue()!
        //  頂点情報の登録
        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 textureLoader = MTKTextureLoader(device: _device)
        let textureLoaderOptions = [
            MTKTextureLoader.Option.textureUsage: NSNumber(value: MTLTextureUsage.shaderRead.rawValue),
            MTKTextureLoader.Option.textureStorageMode: NSNumber(value: MTLStorageMode.`private`.rawValue)
        ]
        _color_image    = try! textureLoader.newTexture(name: "colorImage",
                                                       scaleFactor: 1.0,
                                                       bundle: nil,
                                                       options: textureLoaderOptions)
        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 {
                if let render_encoder = command_buffer.makeRenderCommandEncoder(descriptor: render_pass_descriptor) {
                    //  テクスチャの設定<新規>
                    render_encoder.setFragmentTexture(_color_image,
                                                      index: TextureIndex.color.rawValue)
                    //  モデルの設定
                    render_encoder.setRenderPipelineState(_render_pipeline_state)
                    render_encoder.setVertexBuffer(_buffer_position,
                                                   offset: 0,
                                                   index: 0)
                    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) {
    }
}

■Steop3:テクスチャをアセットに登録

「Assets.xcassets」に右クリックでメニューを出し「New Texture Set」を選択します。
スクリーンショット 2018-11-30 14.22.35.png
「Texture」が作成されます。
スクリーンショット 2018-11-30 14.22.45.png
今回用意したテクスチャ(Pngファイル)
color.png
テクスチャを「All Universel」にドラッグ&ドロップし名前を「colorImage」に変更します。
スクリーンショット 2018-11-30 14.23.08.png

■Setp4:実行

iOsは実機で実行してくださいMacの場合は以下のようになります。
スクリーンショット 2018-11-30 12.30.01.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