Metal Sample Code (Hello Triangle)

通过Metal绘制一个简单的三角形,效果如下:

实现效果.jpeg
Shader

首先,我们需要创建一个.h文件以及.metal文件,

.h文件用于桥接C 与 shader

声明一个顶点输入索引结果体 以及 顶点数据结构体(坐标与颜色) 用于传递给Shader文件

typedef enum CCVertexInputIndex
{
    CCVertexInputIndexVertices     = 0,
    CCVertexInputIndexViewportSize = 1,
} CCVertexInputIndex;

typedef struct
{
    vector_float2 position;
    vector_float4 color;
} CCVertex;

在.metal 文件中实现顶点着色器与片元着色器

  1. 声明一个光栅化数据包含颜色及坐标

     // Vertex shader outputs and fragment shader inputs
     typedef struct
     {
         float4 position [[position]];
    
         float4 color;
    
     } RasterizerData;
    
  2. 实现顶点着色器

     vertex RasterizerData
     vertexShader(uint vertexID [[vertex_id]],
                  constant CCVertex *vertices [[buffer(CCVertexInputIndexVertices)]],
                  constant vector_uint2 *viewportSizePointer [[buffer(CCVertexInputIndexViewportSize)]])
     {
         RasterizerData out;
    
         float2 pixelSpacePosition = vertices[vertexID].position.xy;
         vector_float2 viewportSize = vector_float2(*viewportSizePointer);
         out.position = vector_float4(0.0, 0.0, 0.0, 1.0);
         out.position.xy = pixelSpacePosition / (viewportSize / 2.0);
         out.color = vertices[vertexID].color;
         return out;
     }
    
    
  3. 实现片元着色器

    光栅化数据输入

    Rasterization.png

    fragment float4 fragmentShader(RasterizerData in [[stage_in]])

    返回插值颜色

    Interpolation.png
    fragment float4 fragmentShader(RasterizerData in [[stage_in]])
    {
     // Return the interpolated color.
     return in.color;
    }
    
Renderer

在苹果的HelloTriangle中建议我们创建一个事件处理类Renderer 用于处理

实例方法

声明

- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView

实现

  1. 加载Shader文件

    id defaultLibrary = [_device newDefaultLibrary];
    id vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];
    id fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
    
  2. 通过渲染管线解释器创建渲染管道

    MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
    pipelineStateDescriptor.label = @"Simple Pipeline";
    pipelineStateDescriptor.vertexFunction = vertexFunction;
    pipelineStateDescriptor.fragmentFunction = fragmentFunction;
    pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
    _pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor
    NSAssert(_pipelineState, @"Failed to create pipeline state: %@", error);                                                                 error:&error];
    
  3. 获取一个新的提交队列

     _commandQueue = [_device newCommandQueue];
    
渲染视图协议实现

-(void)drawInMTKView:(nonnull MTKView *)view

  1. 创建顶点数据

    2DTriangleVertices.png
     static const CCVertex triangleVertices[] =
        {
            // 2D positions,    RGBA colors
            { {  250,  -250 }, { 1, 0, 0, 1 } },
            { { -250,  -250 }, { 0, 1, 0, 1 } },
            { {    0,   250 }, { 0, 0, 1, 1 } },
        };
    
  2. 获取命令编码器

      id commandBuffer = [_commandQueue commandBuffer];
        commandBuffer.label = @"MyCommand";
    
        // Obtain a renderPassDescriptor generated from the view's drawable textures.
        MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    
        if(renderPassDescriptor == nil)
        {
            return;
        }
            // Create a render command encoder.
        id renderEncoder =
        [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
         renderEncoder.label = @"MyRenderEncoder";
    
  3. 设置视口

    //5.设置我们绘制的可绘制区域
     /*
     typedef struct {
         double originX, originY, width, height, znear, zfar;
     } MTLViewport;
      */
     //视口指定Metal渲染内容的drawable区域。 视口是具有x和y偏移,宽度和高度以及近和远平面的3D区域
     //为管道分配自定义视口需要通过调用setViewport:方法将MTLViewport结构编码为渲染命令编码器。 如果未指定视口,Metal会设置一个默认视口,其大小与用于创建渲染命令编码器的drawable相同。
     MTLViewport viewPort = {
         0.0,0.0,_viewportSize.x,_viewportSize.y,-1.0,1.0
     };
     [renderEncoder setViewport:viewPort];
    
  4. 设置渲染管道

    [renderEncoder setRenderPipelineState:_pipelineState];
    
  5. 设置参数数据到顶点着色器

      //7.从应用程序OC 代码 中发送数据给Metal 顶点着色器 函数
     //顶点数据+颜色数据
     //   1) 指向要传递给着色器的内存的指针
     //   2) 我们想要传递的数据的内存大小
     //   3)一个整数索引,它对应于我们的“vertexShader”函数中的缓冲区属性限定符的索引。
    
     [renderEncoder setVertexBytes:triangleVertices
                            length:sizeof(triangleVertices)
                           atIndex:CCVertexInputIndexVertices];
    
     //viewPortSize 数据
     //1) 发送到顶点着色函数中,视图大小
     //2) 视图大小内存空间大小
     //3) 对应的索引
     [renderEncoder setVertexBytes:&_viewportSize
                            length:sizeof(_viewportSize)
                           atIndex:CCVertexInputIndexViewportSize];
    
  6. 绘制编码提交

     //8.画出三角形的3个顶点
    // @method drawPrimitives:vertexStart:vertexCount:
    //@brief 在不使用索引列表的情况下,绘制图元
    //@param 绘制图形组装的基元类型
    //@param 从哪个位置数据开始绘制,一般为0
    //@param 每个图元的顶点个数,绘制的图型顶点数量
    /*
     MTLPrimitiveTypePoint = 0, 点
     MTLPrimitiveTypeLine = 1, 线段
     MTLPrimitiveTypeLineStrip = 2, 线环
     MTLPrimitiveTypeTriangle = 3,  三角形
     MTLPrimitiveTypeTriangleStrip = 4, 三角型扇
     */
    
    [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
                      vertexStart:0
                      vertexCount:3];
    
    //9.表示已该编码器生成的命令都已完成,并且从NTLCommandBuffer中分离
    [renderEncoder endEncoding];
    
    //10.一旦框架缓冲区完成,使用当前可绘制的进度表
    [commandBuffer presentDrawable:view.currentDrawable];
    
    
控制器加载Render

_view.device = MTLCreateSystemDefaultDevice();

NSAssert(_view.device, @"Metal is not supported on this device");

_renderer = [[Renderer alloc] initWithMetalKitView:_view];

NSAssert(_renderer, @"Renderer failed initialization");

// Initialize our renderer with the view size
[_renderer mtkView:_view drawableSizeWillChange:_view.drawableSize];

_view.delegate = _renderer;

你可能感兴趣的:(Metal Sample Code (Hello Triangle))