美文网首页
Metal简单案例001

Metal简单案例001

作者: A慢慢懂 | 来源:发表于2020-08-28 16:37 被阅读0次
本次案例的效果图如下所示! image.png

1、前期需要先定义些顶点、视口以及像素空间位置和像素颜色等数据。

typedef enum MTVertexInputIndex{
    //顶点
    MTVertexInputIndexVertex = 0,
    //视口大小
    MTVertexInputIndexViewportSize = 1,
}MTVertexInputIndex;

typedef struct{
    //像素空间的位置
    vector_float2 position;
    //RGBA颜色
    vector_float4 color;
}MTVertex;

2、然后创建Metal文件。

2.1、设置顶点着色函数
//顶点着色函数
vertex RasterizerData
vertexShader(uint vertexID[[vertex_id]],
             constant MTVertex *vertices[[buffer(MTVertexInputIndexVertex)]],
             constant vector_uint2 *viewportSizePointer[[buffer(MTVertexInputIndexViewportSize)]])
{
  .....
}

RasterizerData为返回的值的类型,vertex为函数修饰符,vertexShader函数名,后面加载Metal文件要用到的哟。
后面分别为顶点ID,顶点数据下标,顶点数据对应的窗口位置。

2.2设置片元着色函数

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

3、自定义MTKView

  • -(instancetype)initWithMetalKitView:(MTKView *)mtkView
    在此函数中初始化GPU设备,和加载Metal文件。
//1.初始GPU设备
_device = mtkView.device;

加载Metal文件做需做的事

  • 设置绘制纹理的像素格式colorPixelFormat
  • 加载Metal文件中的顶点函数vertexShader
  • 加载Metal文件中的片元函数fragmentShader
  • 创建管道并且处理渲染过程中的各个顶点和各个片元
  • 创建并获取顶点数据
  • 然后在获取设备的命令队列

在MTKView的代理中,有两个代理需要咱们自己完成下

  • *- (void)mtkView:(nonnull MTKView )view drawableSizeWillChange:(CGSize)size
  • *- (void)drawInMTKView:(nonnull MTKView )view

第一个函数是当视图改变方向或者大小时调用。
第二个函数是当视图需要渲染桢时调用,在此函数中,获取渲染目标,创建渲染命令编码器。绘制可绘制的区域,设置渲染管道,设置顶点缓存区和绑定视口数据,绘图,结束编码,再将渲染完成的目标推送(commit)到GPU。具体代码

//每当视图需要渲染帧时调用
- (void)drawInMTKView:(nonnull MTKView *)view
{
    //1.为当前渲染的每个渲染传递创建一个新的命令缓冲区
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    //指定缓存区名称
    commandBuffer.label = @"MyCommand";
    
    //2. MTLRenderPassDescriptor:一组渲染目标,用作渲染通道生成的像素的输出目标。
    //currentRenderPassDescriptor 从currentDrawable's texture,view's depth, stencil, and sample buffers and clear values.
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    //判断渲染目标是否为空
    if(renderPassDescriptor != nil)
    {
         //创建渲染命令编码器,这样我们才可以渲染到something
        id<MTLRenderCommandEncoder> renderEncoder =
        [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        //渲染器名称
        renderEncoder.label = @"MyRenderEncoder";
        
        //3.设置我们绘制的可绘制区域
        /*
         typedef struct {
         double originX, originY, width, height, znear, zfar;
         } MTLViewport;
         */
        [renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];
        
        //4. 设置渲染管道
        [renderEncoder setRenderPipelineState:_pipelineState];
        
        //5.我们调用-[MTLRenderCommandEncoder setVertexBuffer:offset:atIndex:] 为了从我们的OC代码找发送数据预加载的MTLBuffer 到我们的Metal 顶点着色函数中
        /* 这个调用有3个参数
            1) buffer - 包含需要传递数据的缓冲对象
            2) offset - 它们从缓冲器的开头字节偏移,指示“顶点指针”指向什么。在这种情况下,我们通过0,所以数据一开始就被传递下来.偏移量
            3) index - 一个整数索引,对应于我们的“vertexShader”函数中的缓冲区属性限定符的索引。注意,此参数与 -[MTLRenderCommandEncoder setVertexBytes:length:atIndex:] “索引”参数相同。
         */
        
        //将_vertexBuffer 设置到顶点缓存区中
        [renderEncoder setVertexBuffer:_vertexBuffer
                                offset:0
                               atIndex:CCVertexInputIndexVertices];
        
        //将 _viewportSize 设置到顶点缓存区绑定点设置数据
        [renderEncoder setVertexBytes:&_viewportSize
                               length:sizeof(_viewportSize)
                              atIndex:CCVertexInputIndexViewportSize];
        
        //6.开始绘图
        // @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:_numVertices];
        
        //7/表示已该编码器生成的命令都已完成,并且从NTLCommandBuffer中分离
        [renderEncoder endEncoding];
        
        //8.一旦框架缓冲区完成,使用当前可绘制的进度表
        [commandBuffer presentDrawable:view.currentDrawable];
    }
    
    //9.最后,在这里完成渲染并将命令缓冲区推送到GPU
    [commandBuffer commit];
}

4、在VC中添加MTKView

_view = [[MTKView alloc]initWithFrame:self.view.frame device:MTLCreateSystemDefaultDevice()];
    _render = [[MTRender alloc]initWithMetalKitView:_view];
    if(!_render)
    {
        NSLog(@"Renderer failed initialization");
        return;
    }
    [_render mtkView:_view drawableSizeWillChange:_view.drawableSize];
    _view.delegate = _render;
    [self.view addSubview:_view];

具体代码链接:https://github.com/zhaimengting/MetalBasicBuffers

相关文章

网友评论

      本文标题:Metal简单案例001

      本文链接:https://www.haomeiwen.com/subject/miilsktx.html