Metal入门学习:绘制纹理图片

news2025/1/13 10:11:24
一、编程指南PDF下载链接(中英文档)
  • 1、Metal编程指南PDF链接
    https://github.com/dennie-lee/ios_tech_record/raw/main/Metal学习PDF/Metal 编程指南.pdf

  • 2、Metal着色语言(Metal Shader Language:简称MSL)编程指南PDF链接
    https://github.com/dennie-lee/ios_tech_record/raw/main/Metal学习PDF/Metal 着色语言指南.pdf

二、渲染流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nKCZKRkF-1684659166488)(media/16846393010432/16846502983624.jpg)]

三、内容前述

此示例展示如何配置渲染管线并将其用作渲染通道的一部分,以将简单的图片渲染到视图中和立体锥体中,效果图如下:
在这里插入图片描述

四、Metal渲染管线

渲染管线处理绘图命令并将数据写入渲染通道的目标。此示例侧重于管道的三个主要阶段:顶点阶段、光栅化阶段和片段阶段。在这里插入图片描述
渲染从绘图命令开始,其中包括顶点数和要渲染的图元类型。例如,这是此示例中的绘图命令:(参数数据解析请看下一步)

commandEncoder.drawIndexedPrimitives(type: .triangle, indexCount: 6,
                                     indexType: .uint32, indexBuffer: vertexIndexs,
                                     indexBufferOffset: 0)
五、Metal渲染管道处理数据

这里使用的是顶点索引的方式关联顶点数据。例如顶点数据:

var vertexs:[Float] = [
   //顶点坐标 //纹理坐标 //颜色值
    1, -1,  1,1,  1.0,0.0,0.0,1.0,//长方形的右下角
    -1,-1,  0,1,  1.0,0.0,0.0,1.0,//长方形的左下角
    -1, 1,  0,0,  1.0,0.0,0.0,1.0,//长方形的左上角
    1,  1,  1,0,  1.0,0.0,0.0,1.0,//长方形的右上角
]

顶点参考坐标系如图一,因为只是把图片渲染到视图上,相当于是2D坐标(二维),只看最前面那个面即可;纹理坐标参考图二
在这里插入图片描述

颜色值有四个通道RGBA:红色、绿色、蓝色、透明度,所以是四个数据。

顶点索引值数据,索引0、1、2对应的顶点以顺时针的顺序构成一个三角形;0、2、3对应的顶点以顺时针的顺序构成一个三角形,两个三角形组合起来渲染构成长方形(Metal默认是以顺时针的方式渲染,也可以设置成逆时针,此处使用默认的方式):
在这里插入图片描述

let indexs : [Int32] = [0,1,2,0,2,3]
vertexIndexs = device?.makeBuffer(bytes: indexs,
                                  length: MemoryLayout<Int32>.size * 6,
                                  options: .storageModeShared)

MTLPrimitiveType绘制类型有如下五种:(第四步的参数1)

@available(iOS 8.0, *)
public enum MTLPrimitiveType : UInt, @unchecked Sendable { 
    case point = 0
    case line = 1
    case lineStrip = 2
    case triangle = 3
    case triangleStrip = 4
}

绘制方式如下图所示:
在这里插入图片描述

在此示例中,管道的输入数据是顶点的位置、纹理坐标位置和自定义的颜色(自定义颜色:可堆叠在图片上)
声明一个VertexInput结构,保存顶点坐标、纹理坐标和颜色数据

typedef struct {
    //顶点坐标
    float2 position;
    //纹理坐标
    float2 textureCoordinate;
    //颜色
    float4 color;
} VertexInput;

顶点阶段为顶点生成数据,需要提供变换后的位置和颜色。声明一个包含顶点位置、纹理位置和颜色值的RasterizerData结构

typedef struct {
    //顶点坐标
    float4 position [[position]];
    //纹理坐标
    float2 textureCoordinate;
    //颜色
    float4 color;
} RasterizerData;

位置声明为float4意味着它包含四个32位浮点值(将保存x、y坐标、z左边和w值);纹理位置声明为float2意味着它包含两个32位浮点值(将保存x和y坐标);颜色使用float4存储,因为它们有四个通道:红色、绿色、蓝色和alpha。

因为Metal需要知道光栅化数据中的哪个字段提供位置数据,而Metal不会对结构中的字段强制执行任何特定的命名约定,所以使用[[position]]属性限定符注释位置字段以声明该字段保存输出位置。

六、定义和编写顶点着色函数

声明顶点函数,包括它的输入参数和它输出的数据。就像使用kernel关键字声明计算函数一样,使用vertex关键字声明顶点函数。(kernel:可查看这篇文章https://blog.csdn.net/qqwyuli/article/details/130785820)

//[[vertex_id]] :顶点id标识符,并不由开发者传递
//属性修饰符"[[buffer(index)]]" 为着色函数参数设定了缓存的位置
vertex RasterizerData vertexShader(uint vertexId [[vertex_id]],
                                   constant VertexInput *vertexs [[buffer(0)]]){
    RasterizerData out;
    out.position = vector_float4(0.0,0.0,0.0,1.0);
    out.position.xy = vertexs[vertexId].position;
    //纹理坐标
    out.textureCoordinate = vertexs[vertexId].textureCoordinate;
    //自定义传过来的颜色
    out.color = vertexs[vertexId].color;
    return out;
};

第一个参数vertexID使用[[vertex_id]]属性限定符,Metal的一个关键字。执行渲染命令时,GPU会多次调用顶点函数,为每个顶点生成一个唯一值。

第二个参数 vertices 是一个包含顶点数据的数组,使用之前定义的Vertex结构
第二个具有[[buffer(n)]]属性限定符。默认情况下,Metal会自动为每个参数分配参数表中的槽。当将[[buffer(n)]]限定符添加到缓冲区参数时,明确地告诉Metal要使用哪个插槽。显式声明插槽可以更轻松地修改着色器,而无需更改应用程序代码.

对命令的参数进行编码时,设置顶点函数的参数

commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
七、计算纹理位置和编写片段着色函数(片元着色函数)

对纹理进行采样以从纹理中的某个位置计算颜色。要对纹理数据进行采样,片段函数需要纹理坐标和对要采样的纹理的引用。除了从光栅化器阶段传入的参数外,还传入一个带有 texture2d 类型和 [[texture(index)]] 属性限定符的colorTexture参数。此参数是对要采样的 MTLTexture 对象的引用。

fragment float4 fragmentShader(RasterizerData input [[stage_in]],
                               texture2d<float> colorTexture [[texture(0)]])

使用内置纹理 sample() 函数对纹素数据进行采样。sample()函数有两个参数:一个采样器 (textureSampler),它描述了希望如何采样纹理,以及纹理坐标(input.textureCoordinate),它描述了纹理中要采样的位置。sample()函数从纹理中获取一个或多个像素并返回从这些像素计算出的颜色。

当渲染到的区域与纹理的大小不同时,采样器可以使用不同的算法来准确计算sample()函数应该返回的纹素颜色。设置mag_filter模式来指定当面积大于纹理大小时采样器应该如何计算返回的颜色,设置min_filter模式来指定当面积小于纹理大小时采样器应该如何计算返回颜色质地。为两个过滤器设置线性模式会使采样器对给定纹理坐标周围像素的颜色进行平均,从而产生更平滑的输出图像。

constexpr sampler textureSampler (mag_filter::linear,min_filter::linear);
//float4 color = colorTexture.sample(textureSampler, input.textureCoordinate) * input.color;
float4 color = colorTexture.sample(textureSampler, input.textureCoordinate);

对命令的参数进行编码时,设置片段函数的纹理参数。此示例使用索引0来识别Metal着色语言代码中的纹理。

commandEncoder.setFragmentTexture(texture, index: 0)

完整的片段函数

fragment float4 fragmentShader(RasterizerData input [[stage_in]],
                               texture2d<float> colorTexture [[texture(0)]]){
    constexpr sampler textureSampler (mag_filter::linear,min_filter::linear);
    //float4 color = colorTexture.sample(textureSampler, input.textureCoordinate) * input.color;
    float4 color = colorTexture.sample(textureSampler, input.textureCoordinate);
    return color;
};
八、创建渲染Pipeline State对象

顶点函数和片元函数已经完成,可以创建一个使用它们的渲染管道。首先,获取默认库并为每个函数获取一个MTLFunction对象和创建一个 MTLRenderPipelineState 对象。渲染管道有更多阶段需要配置,可以使用 MTLRenderPipelineDescriptor 来配置管道。

var library = device?.makeDefaultLibrary()
if let url = Bundle.main.url(forResource: "TextureShaders", withExtension: "metal"){
    library = try? device?.makeLibrary(URL: url)
}
let descriptor = MTLRenderPipelineDescriptor()
descriptor.vertexFunction = library?.makeFunction(name: "vertexShader")
descriptor.fragmentFunction = library?.makeFunction(name: "fragmentShader")
descriptor.colorAttachments[0].pixelFormat = viewColorPixelformat
pipelineState = try? device?.makeRenderPipelineState(descriptor: descriptor)
九、设置视口

现在有了管道的渲染管道状态对象,将要渲染图片纹理。可以使用渲染命令编码器来执行此操作。首先,设置视口,以便 Metal 知道要绘制到渲染目标的哪一部分。

commandEncoder.setViewport(MTLViewport(originX: 0, originY: 0,
                                       width: viewSize.width, height: viewSize.height,
                                       znear: -1.0, zfar: 1.0))
十、设置渲染Pipeline State

为要使用的管道设置渲染管道状态。

commandEncoder.setRenderPipelineState(pipelineState)
十一、将参数数据发送到顶点函数

通常使用缓冲区(MTLBuffer)将数据传递给着色器。然而,当只需要将少量数据传递给顶点函数时,就像这里的情况一样,将数据直接复制到命令缓冲区中。

commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
十二、对绘图命令进行编码

指定图元的种类、起始索引和索引数。渲染纹理图片时,将使用vertexID参数的值和顶点索引调用顶点函数。

commandEncoder.drawIndexedPrimitives(type: .triangle, indexCount: 6,
                                     indexType: .uint32, indexBuffer: vertexIndexs,
                                     indexBufferOffset: 0)

与使用Metal绘制到屏幕一样,结束编码过程并提交命令缓冲区。但是,可以使用同一组步骤对更多渲染命令进行编码。最终图像呈现为好像命令是按照指定的顺序处理的。(为了性能,允许 GPU 并行处理命令甚至部分命令,只要最终结果看起来是按顺序呈现的。)

十三、完整代码

例子:github链接:https://github.com/dennie-lee/MetalDrawTextureDemo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/551570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux服务器丨重测序数据分析常用软件安装指南

重测序分析软件安装指南 重测序&#xff08;resequencing&#xff09;是指对已知基因组进行高通量测序&#xff0c;以检测个体或种群的遗传变异&#xff0c;从而研究基因组的结构和功能。与全基因组测序不同&#xff0c;重测序通常只对一部分基因组进行测序&#xff0c;例如外显…

【2023 雷泽杯 · Misc】我是签到题

一道图片隐写题 目录 一、题目 二、思路 1.010editor查看源码 2.检索头部关键字段 3.图片隐写——高度隐写 一、题目 看不到这个图片对吧&#xff0c;这就是题目原本的样子。 二、思路 1.010editor查看源码 很明显的rar特征&#xff0c;尝试将后缀改成rar后打开。 发…

《Java就业班体系结构.pdf》:从入门到精通,掌握Java开发的终极指南,成为熟练高级开发者!

Java开发的终极指南 第1阶段&#xff1a;JAVA开篇第2阶段&#xff1a;JAVA语言语法第3阶段&#xff1a;集成开发工具的使用第4阶段&#xff1a;面向对象第5阶段&#xff1a;JavaSE进阶学习第6阶段&#xff1a;数据库JDBC第7阶段&#xff1a;前端精讲第8阶段&#xff1a;JavaEE第…

RocketMQ的学习历程(4)----消息处理 (2)

1.消费者的两种消费模式 顺序消费模式&#xff08;Sequential Consumer Mode&#xff09;&#xff1a; 在顺序消费模式下&#xff0c;消息队列中的消息按照发送的顺序被消费者顺序消费。每个消息队列只会被一个消费者线程消费&#xff0c;确保消息的顺序性。这种模式适用于需要…

【Hadoop】三、数据仓库基础与Apache Hive入门

文章目录 三、数据仓库基础与Apache Hive入门1、数据仓库基本概念1.1、数据仓库概念1.2、场景案例&#xff1a;数据仓库为何而来1.3、数据仓库主要特征1.4、数据仓库主流开发语言--SQL 2、Apache Hive入门2.1、Apache Hive概述2.2、场景设计&#xff1a;如何模拟实现Hive功能2.…

深度学习用于医学预后-第二课第四周16-17节-比较两个患者的风险

我们怎样比较两个患者的风险&#xff1f; 让我们谈谈如何比较两名患者的风险。假设我们有两个病人&#xff0c;一个50岁&#xff0c;血压162&#xff0c;另一个61岁&#xff0c;血压140。 我们可以使用生存树首先找出他们所属的组。所以我们看到第一个病人的年龄小于60&#…

【CSS 选择器应用在QSS】第二天

CSS 选择器应用在QSS 【1】元素选择器&#xff08;元素通用性&#xff09;【2】id 选择器&#xff08;唯一性&#xff09;【2.1】CSS【2.2】QSS 【3】类选择器【3.1】CSS【3.2】QSS 【4】类选择器&#xff08;只针对指定元素&#xff09;【4.1】CSS【4.2】QSS 【5】通用选择器【…

iptables 防火墙二

目录 SNAT 原理与应用SNAT原理&#xff1a;修改数据包的源地址。 SNAT 实验DNAT原理与应用DNAT原理&#xff1a;修改数据包的目的地址。DNAT转换前提条件&#xff1a; DNAT 示例 SNAT 原理与应用 SNAT 应用环境&#xff1a;局域网主机共享单个公网IP地址接入Internet&#xff…

MyBatis技术练习

一、模仿教程练习增删改查&#xff0c;自己完成一个新表相关操作 1、配置fkxml文件 我们这里的增删改查sql语句必须对应我们自己创建的表 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.…

实测有效:由于找不到MSVCP140.dll,无法继续执行代码

从解决实际问题的角度上&#xff0c;推荐两种实测有效的方法。 先来说一下msvcp140.dll是什么&#xff1f; msvcp140.dll 是 Microsoft Visual C Redistributable for Visual Studio 2015 库文件的一部分。这个文件是一些需要 Visual Studio 2015 支持的程序所必需的。 如果…

(C语言版)力扣(LeetCode)题库1-5题解析

力扣&#xff08;LeetCode&#xff09;题库1-5题解析 1.两数之和题目解析 2.两数相加题目解法 3.无重复字符的最长字串题目解法 4. 寻找两个正序数组的中位数题目解法 5. 最长回文子串题目解法 结语 1.两数之和 题目 给定一个整数数组 nums 和一个整数目标值 target&#xff…

Java基础--->并发部分(3)【JUC、AQS】

文章目录 AQS&#xff08;AbstractQueuedSynchronizer&#xff09;AQS实现原理AQS操作重点方法 Java并发容器JUC&#xff08;java.util.concurrent&#xff09;ConcurrentHashMapCopyOnWriteArrayList AQS&#xff08;AbstractQueuedSynchronizer&#xff09; AbstractQueuedSy…

如何从其他ETL工具迁移到ETLCloud上?

ETL数据集成工具主要用于将来自不同数据源的数据整合到一个单一的、一致的数据存储库或将数据分发到不同的数据源中&#xff0c;同时也可以把数仓中的数据通过ETL反向输出给业务系统使用。它可以帮助企业解决数据共享问题&#xff0c;同时有效地管理和利用海量数据&#xff0c;…

DAY 61 MySQL高级SQL语句

高级SQL语句&#xff08;进阶查询&#xff09; 先准备2个表 一个location表 use market;create table location(Region char(20),Store_Name char(20));insert into location values(East,Boston);insert into location values(East,New York);insert into location values(W…

python数据可视化显示(附代码)

Python是一种非常流行的编程语言&#xff0c;具有广泛的应用领域&#xff0c;包括数据可视化。在数据可视化中&#xff0c;Python提供了多种工具来帮助用户创建各种类型的图表、图形和可视化效果。本文将介绍Python数据可视化的基本概念、工具和技术&#xff0c;并提供代码示例…

CustomTkinter:【二】颜色和主题、外观模式、缩放、包装

GitHub地址: https://github.com/TomSchimansky/CustomTkinter 官网&#xff1a; https://customtkinter.tomschimansky.com/ 官方教程文档&#xff1a;https://customtkinter.tomschimansky.com/documentation/ 目录 1、颜色和主题2 、外观模式3 、缩放4、包装 1、颜色和主题 …

2023/5/21周报

目录 摘要 论文阅读 1、标题和现存问题 2、各个结构 3、基于GNN-LSTM-CNN 网络轨迹预测模型 4、实验准备 5、实验结果 深度学习 1、费舍尔判别 2、步骤具体化 3、GCN 总结 摘要 本周在论文阅读上&#xff0c;阅读了一篇基于GNN-LSTM-CNN网络的6G车辆轨迹预测算法的…

git pull报没有足够内存 not enough memory for initialization

git clone 或 git pull 批量同步远程 git仓库代码时&#xff0c;报 没有足够内存用于初始化 not enough memory for initialization。经过观察 资源管理器 的内存使用情况&#xff0c;发现为 剩余可用内存不足造成的。加物理内存麻烦&#xff0c;可通过适当调整 分页文件&…

chatgpt赋能Python-pythoncom安装

Pythoncom安装指南 如果你是一位Python编程的爱好者或专业工程师&#xff0c;那么你可能会需要使用Pythoncom库。Pythoncom是Python与COM技术相互操作的重要组件&#xff0c;它可以帮助你实现各种Windows应用程序与Python之间的无缝集成。 什么是Pythoncom Pythoncom是Pytho…

电商项目9:新增商品

电商项目9&#xff1a;新增商品 1、前端1.1、修复前端组件通信问题1.2、引入其他前端代码1.3、会员等级列表1.4、当前分类关联的所有品牌 2、后端2.1、会员系统搭建&#xff08;注册与发现&#xff09;2.2、当前分类关联的所有品牌2.3、获取分类下所有分组&关联属性 1、前端…