1.Metal 简介

Metal 是针对 iPhone 和 iPad 中 GPU 编程的高度优化的框架。其名字来源是因为 Metal 是 iOS 平台中最底层的图形框架 (意指 “最接近硬件”)。

优点:

  1. cpu 使用率低;
  2. Metal 将苹果的GPU 发挥最⼤性能;
  3. 最⼤限度CPU/GPU 并发性;
  4. 方便管理我们资源

2. 函数解析

2.1

//每当视图改变方向或调整大小时调用(void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size;

2.2

//每当视图需要渲染帧时调用
- (void)drawInMTKView:(nonnull MTKView *)view

3. 绘制三角形


注:苹果官方建议Metal渲染类与ViewController类分开实现,所以下面我们在ViewController类中实现MTKView的加载,在Metal渲染类中实现渲染逻辑。

3.1创建并编辑Metal文件

  1. command + N --> Metal File 创建metal着色器文件

  2. 定义顶点着色器输入和片元着色器输入,相当于OpenGL ES中的varying修饰的变量,即桥接变量。

// 顶点着色器输出和片段着色器输入
//结构体
typedef struct
{//处理空间的顶点信息float4 clipSpacePosition [[position]];//颜色float4 color;} RasterizerData;
  1. 定义顶点着色器函数 和 片元着色器函数
//顶点着色函数
vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],constant Vertex *vertices [[buffer(VertexInputIndexVertices)]],constant vector_uint2 *viewportSizePointer [[buffer(VertexInputIndexViewportSize)]])
{/*处理顶点数据:1) 执行坐标系转换,将生成的顶点剪辑空间写入到返回值中.2) 将顶点颜色值传递给返回值*///定义outRasterizerData out; //    //初始化输出剪辑空间位置
//    out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);
//
//    // 索引到我们的数组位置以获得当前顶点
//    // 我们的位置是在像素维度中指定的.
//    float2 pixelSpacePosition = vertices[vertexID].position.xy;
//
//    //将vierportSizePointer 从verctor_uint2 转换为vector_float2 类型
//    vector_float2 viewportSize = vector_float2(*viewportSizePointer);
//
//    //每个顶点着色器的输出位置在剪辑空间中(也称为归一化设备坐标空间,NDC),剪辑空间中的(-1,-1)表示视口的左下角,而(1,1)表示视口的右上角.
//    //计算和写入 XY值到我们的剪辑空间的位置.为了从像素空间中的位置转换到剪辑空间的位置,我们将像素坐标除以视口的大小的一半.
//    out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0);out.clipSpacePosition = vertices[vertexID].position;//把我们输入的颜色直接赋值给输出颜色. 这个值将于构成三角形的顶点的其他颜色值插值,从而为我们片段着色器中的每个片段生成颜色值.out.color = vertices[vertexID].color;//完成! 将结构体传递到管道中下一个阶段:return out;
}//当顶点函数执行3次,三角形的每个顶点执行一次后,则执行管道中的下一个阶段.栅格化/光栅化.// 片元函数
//[[stage_in]],片元着色函数使用的单个片元输入数据是由顶点着色函数输出.然后经过光栅化生成的.单个片元输入函数数据可以使用"[[stage_in]]"属性修饰符.
//一个顶点着色函数可以读取单个顶点的输入数据,这些输入数据存储于参数传递的缓存中,使用顶点和实例ID在这些缓存中寻址.读取到单个顶点的数据.另外,单个顶点输入数据也可以通过使用"[[stage_in]]"属性修饰符的产生传递给顶点着色函数.
//被stage_in 修饰的结构体的成员不能是如下这些.Packed vectors 紧密填充类型向量,matrices 矩阵,structs 结构体,references or pointers to type 某类型的引用或指针. arrays,vectors,matrices 标量,向量,矩阵数组.
fragment float4 fragmentShader(RasterizerData in [[stage_in]])
{//返回输入的片元颜色return in.color;
}

3.2创建C 与 OC的桥接文件

该头文件的目的是为了c代码与OC代码可以共享与 shader 和 C 代码 为了确保Metal Shader缓存区索引能够匹配 Metal API Buffer 设置的集合调用

/*介绍:头文件包含了 Metal shaders 与C/OBJC 源之间共享的类型和枚举常数
*/#ifndef ShaderTypes_h
#define ShaderTypes_h// 缓存区索引值 共享与 shader 和 C 代码 为了确保Metal Shader缓存区索引能够匹配 Metal API Buffer 设置的集合调用
typedef enum VertexInputIndex
{//顶点VertexInputIndexVertices     = 0,//视图大小VertexInputIndexViewportSize = 1,
} VertexInputIndex;//结构体: 顶点/颜色值
typedef struct
{// 像素空间的位置// 像素中心点(100,100)vector_float4 position;// RGBA颜色vector_float4 color;
} Vertex;#endif

3.3 ViewController类

 - (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view, typically from a nib.//1. 获取_view_view = (MTKView *)self.view;//2.为_view 设置MTLDevice(必须)//一个MTLDevice 对象就代表这着一个GPU,通常我们可以调用方法MTLCreateSystemDefaultDevice()来获取代表默认的GPU单个对象._view.device = MTLCreateSystemDefaultDevice();//3.判断是否设置成功if (!_view.device) {NSLog(@"Metal is not supported on this device");return;}//4. 创建Renderer//分开你的渲染循环://在我们开发Metal 程序时,将渲染循环分为自己创建的类,是非常有用的一种方式,使用单独的类,我们可以更好管理初始化Metal,以及Metal视图委托._render =[[Renderer alloc]initWithMetalKitView:_view];//5.判断_render 是否创建成功if (!_render) {NSLog(@"Renderer failed initialization");return;}//6.设置MTKView 的代理(由Renderer来实现MTKView 的代理方法)_view.delegate = _render;//7.视图可以根据视图属性上设置帧速率(指定时间来调用drawInMTKView方法--视图需要渲染时调用)_view.preferredFramesPerSecond = 60;}

3.4 Metal渲染类

设置MTKView,准备工作

//初始化MTKView
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView
{self = [super init];if(self){NSError *error = NULL;//1.获取GPU 设备_device = mtkView.device;//2.在项目中加载所有的(.metal)着色器文件// 从bundle中获取.metal文件id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];//从库中加载顶点函数id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"];//从库中加载片元函数id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];//3.配置用于创建管道状态的管道MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];//管道名称pipelineStateDescriptor.label = @"Simple Pipeline";//可编程函数,用于处理渲染过程中的各个顶点pipelineStateDescriptor.vertexFunction = vertexFunction;//可编程函数,用于处理渲染过程中各个片段/片元pipelineStateDescriptor.fragmentFunction = fragmentFunction;//一组存储颜色数据的组件pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;//4.同步创建并返回渲染管线状态对象_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];//判断是否返回了管线状态对象if (!_pipelineState){//如果我们没有正确设置管道描述符,则管道状态创建可能失败NSLog(@"Failed to created pipeline state, error %@", error);return nil;}//5.创建命令队列_commandQueue = [_device newCommandQueue];}return self;
}

绘制

//每当视图需要渲染帧时调用
- (void)drawInMTKView:(nonnull MTKView *)view
{//1. 顶点数据/颜色数据static const Vertex triangleVertices[] ={//顶点,    RGBA 颜色值{ {  0.5, -0.25, 0.0, 1.0 }, { 1, 0, 0, 1 } },{ { -0.5, -0.25, 0.0, 1.0 }, { 0, 1, 0, 1 } },{ { -0.0f, 0.25, 0.0, 1.0 }, { 0, 0, 1, 1 } },};//2.为当前渲染的每个渲染传递创建一个新的命令缓冲区id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];//指定缓存区名称commandBuffer.label = @"MyCommand";//3.// MTLRenderPassDescriptor:一组渲染目标,用作渲染通道生成的像素的输出目标。MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;//判断渲染目标是否为空if(renderPassDescriptor != nil){//4.创建渲染命令编码器,这样我们才可以渲染到somethingid<MTLRenderCommandEncoder> renderEncoder =[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];//渲染器名称renderEncoder.label = @"MyRenderEncoder";//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];//[renderEncoder setViewport:(MTLViewport){0.0, 0.0, _viewportSize.x, _viewportSize.y, -1.0, 1.0 }];//6.设置当前渲染管道状态对象[renderEncoder setRenderPipelineState:_pipelineState];//7.从应用程序OC 代码 中发送数据给Metal 顶点着色器 函数//顶点数据+颜色数据//   1) 指向要传递给着色器的内存的指针//   2) 我们想要传递的数据的内存大小//   3)一个整数索引,它对应于我们的“vertexShader”函数中的缓冲区属性限定符的索引。[renderEncoder setVertexBytes:triangleVerticeslength:sizeof(triangleVertices)atIndex:VertexInputIndexVertices];//viewPortSize 数据//1) 发送到顶点着色函数中,视图大小//2) 视图大小内存空间大小//3) 对应的索引[renderEncoder setVertexBytes:&_viewportSizelength:sizeof(_viewportSize)atIndex:VertexInputIndexViewportSize];//8.画出三角形的3个顶点// @method drawPrimitives:vertexStart:vertexCount://@brief 在不使用索引列表的情况下,绘制图元//@param 绘制图形组装的基元类型//@param 从哪个位置数据开始绘制,一般为0//@param 每个图元的顶点个数,绘制的图型顶点数量/*MTLPrimitiveTypePoint = 0, 点MTLPrimitiveTypeLine = 1, 线段MTLPrimitiveTypeLineStrip = 2, 线环MTLPrimitiveTypeTriangle = 3,  三角形MTLPrimitiveTypeTriangleStrip = 4, 三角型扇*/[renderEncoder drawPrimitives:MTLPrimitiveTypeTrianglevertexStart:0vertexCount:3];//9.表示已该编码器生成的命令都已完成,并且从NTLCommandBuffer中分离[renderEncoder endEncoding];//10.一旦框架缓冲区完成,使用当前可绘制的进度表[commandBuffer presentDrawable:view.currentDrawable];}//11.最后,在这里完成渲染并将命令缓冲区推送到GPU[commandBuffer commit];
}

Metal(一) 三角形绘制相关推荐

  1. 【OpenGL】十五、OpenGL 绘制三角形 ( 绘制 GL_TRIANGLE_FAN 三角形扇 )

    文章目录 一.绘制 GL_TRIANGLE_FAN 三角形 1.绘制 3 个点的情况 2.绘制 4 个点的情况 3.绘制 5 个点的情况 4.绘制 6 个点的情况 二.相关资源 一.绘制 GL_TRI ...

  2. 【OpenGL】十四、OpenGL 绘制三角形 ( 绘制 GL_TRIANGLE_STRIP 三角形 | GL_TRIANGLE_STRIP 三角形绘制分析 )

    文章目录 一.绘制 GL_TRIANGLE_STRIP 三角形 二.GL_TRIANGLE_STRIP 三角形绘制分析 三.相关资源 一.绘制 GL_TRIANGLE_STRIP 三角形 该模式绘制首 ...

  3. 【OpenGL】十三、OpenGL 绘制三角形 ( 绘制单个三角形 | 三角形绘制顺序 | 绘制多个三角形 )

    文章目录 一.绘制三角形 二.三角形绘制顺序 1.绘制正面 2.三个点逆时针方向排列 3.三个点顺时针方向排列 4.设置点的正面方向 三.绘制多个三角形 四.相关资源 一.绘制三角形 三角形绘制即绘制 ...

  4. 【DirectX12】2.示例三角形绘制

    示例三角形绘制 1.效果 下面只贴出关于dx的代码,有时间再详细说明. 2.标头.h #pragma once#include "pch.h" #include "LVE ...

  5. 前端怎么画三角形_WEB前端-CSS三角形绘制方法

    我们的网页因为 CSS 而呈现千变万化的风格.这一看似简单的样式语言在使用中非常灵活,只要你发挥创意就能实现很多比人想象不到的效果.特别是随着 CSS3 的广泛使用,更多新奇的 CSS 作品涌现出来. ...

  6. WIN32 opengl三角形绘制

    #include "stdafx.h" #include "Win32Project1.h" #include "glut.h"#defin ...

  7. Opengl ES之三角形绘制

    在前面我们已经在NDK层搭建好了EGL环境,也介绍了一些着色器相关的理论知识,那么这次我们就使用已经搭配的EGL绘制一个三角形吧. 在Opengl ES的世界中,无论多复杂的形状都是由点.线或三角形组 ...

  8. Metal之渲染绘制三角形

    准备工作 Metal渲染的构建流程, 请参考:Metal之简单渲染动态切换屏幕颜色 Metal三角形的渲染显示与渲染构建流程大体一致, 本文主要介绍以下方面的修改和实现: ① metal渲染文件 ② ...

  9. Python实验舱谢尔宾斯基三角形绘制教程

    三角形,大家应该再熟悉不过了. 上图是一个由四个小三角形拼成的大三角形,也叫谢尔宾斯基三角形. 谢尔宾斯基三角形还能继续画下去: 2阶(上图是1阶): 3阶: 5阶: 8阶: (小编奉劝一句:不要试图 ...

最新文章

  1. 修改Activity响应音量控制键修改的音频流
  2. 最全干货:从写简历,到面试、谈薪酬的那些技巧和防坑指南
  3. 视觉三位重建:计算机视觉的核心任务
  4. [转]Backup and restore history details
  5. 浅谈安卓线程池相关问题
  6. C语言,去你的策略模式!
  7. samba 设置文件的读写权限
  8. centos7进入单用户模式
  9. 【Hive】hive表的文件存储格式
  10. MySQL数据库开发的三十六条军规
  11. RiceQuant开源项目Rqalpha运行结果文件result.pkl读取
  12. SOLIDWORKS Composer制作动画的方法
  13. dota2服务器位置设置在哪里,《DOTA2》自走棋国服怎么进入 自走棋国服服务器进入方法...
  14. WinDynamicDesktop下载慢解决方法
  15. 同步多个 git 远程仓库
  16. Java语言高级-常用API-第四节 ArrayList集合
  17. 我所知道的中国NLP「破圈」十年
  18. 一、基于wifi控制的智能家居系统之项目简介和设计方案(硬件基于arduino+esp8266,软件Android+Web端+scoket服务器,实现语音控制)
  19. Python对表格操作
  20. 在线编辑php文件的代码

热门文章

  1. 读懂 x86 架构 Linux 系统虚拟化,这文不容错过
  2. ###《High-level event recognition in unconstrained videos》
  3. import 下划线作用
  4. 使用PHPExcel类库编写一个快捷的导出函数
  5. remix中错误集合
  6. 【python机器学习基础教程】(四)
  7. Kafka+zookeeper集群搭建
  8. python爬取天猫,python如何爬取天猫店铺商品链接?
  9. 计算机一级云居寺,刁常宇-Zhejiang University Personal homepage
  10. 6大多人协作工具推荐