Metal Sample Code (Hello Triang
2020-08-22 本文已影响0人
_涼城
通过Metal
绘制一个简单的三角形,效果如下:

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 文件中实现顶点着色器与片元着色器
-
声明一个光栅化数据包含颜色及坐标
// Vertex shader outputs and fragment shader inputs typedef struct { float4 position [[position]]; float4 color; } RasterizerData;
-
实现顶点着色器
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; }
-
实现片元着色器
光栅化数据输入
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
用于处理<MTKViewDelegate>
。
实例方法
声明
- (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView
实现
-
加载Shader文件
id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary]; id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
-
通过渲染管线解释器创建渲染管道
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];
-
获取一个新的提交队列
_commandQueue = [_device newCommandQueue];
渲染视图协议实现
-(void)drawInMTKView:(nonnull MTKView *)view
-
创建顶点数据
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 } }, };
-
获取命令编码器
id<MTLCommandBuffer> 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<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 setRenderPipelineState:_pipelineState];
-
设置参数数据到顶点着色器
//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];
-
绘制编码提交
//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;