
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];
网友评论