metal 初始和画三角形
本文 https://github.com/AlanGeIT/Metal
另外一个 https://github.com/loyinglin/LearnMetal
@interface Renderer : NSObject<MTKViewDelegate> 要继承这个接口
1、helloworld 设置清屏颜色
属性
id<MTLDevice> _device;
id<MTLCommandQueue> _commandQueue;
初始化
_device = mtkView.device;
_commandQueue = [_device newCommandQueue];
/// 每当视图需要渲染时调用
- (void)drawInMTKView:(nonnull MTKView *)view { Color color = [self makeFancyColor]; // 设置清屏颜色 view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha); id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer]; commandBuffer.label = @"MyCommand"; MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; if(renderPassDescriptor != nil) { id<MTLRenderCommandEncoder> renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; renderEncoder.label = @"MyRenderEncoder"; [renderEncoder endEncoding]; [commandBuffer presentDrawable:view.currentDrawable]; } // 在这里完成渲染并将命令缓冲区提交给GPU [commandBuffer commit]; }
2、
四个属性
@implementation AAPLRenderer
{
//我们用来渲染的设备(又名GPU)
id<MTLDevice> _device;
// 我们的渲染管道有顶点着色器和片元着色器 它们存储在.metal shader 文件中
id<MTLRenderPipelineState> _pipelineState;
//命令队列,从命令缓存区获取
id<MTLCommandQueue> _commandQueue;
//当前视图大小,这样我们才可以在渲染通道使用这个视图
vector_uint2 _viewportSize;
}
//初始化MTKView - (nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)mtkView { self = [super init]; if(self) { NSError *error = NULL; _device = mtkView.device; // 在项目中加载所有的(.metal)着色器文件 // 从bundle中获取.metal文件 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 error:&error]; //判断是否返回了管线状态对象 if (!_pipelineState) { //如果我们没有正确设置管道描述符,则管道状态创建可能失败 NSLog(@"Failed to created pipeline state, error %@", error); return nil; } // 创建命令队列 _commandQueue = [_device newCommandQueue]; } return self; }
metal
#include <metal_stdlib> //使用命名空间 Metal using namespace metal; // 导入Metal shader 代码和执行Metal API命令的C代码之间共享的头 #import "AAPLShaderTypes.h" // 顶点着色器输出和片段着色器输入 //结构体 typedef struct { //处理空间的顶点信息 float4 clipSpacePosition [[position]]; //颜色 float4 color; } RasterizerData; //顶点着色函数 vertex RasterizerData vertexShader(uint vertexID [[vertex_id]], constant AAPLVertex *vertices [[buffer(AAPLVertexInputIndexVertices)]], constant vector_uint2 *viewportSizePointer [[buffer(AAPLVertexInputIndexViewportSize)]]) { out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0); float2 pixelSpacePosition = vertices[vertexID].position.xy; vector_float2 viewportSize = vector_float2(*viewportSizePointer); out.clipSpacePosition.xy = pixelSpacePosition / (viewportSize / 2.0); out.color = vertices[vertexID].color; return out; } fragment float4 fragmentShader(RasterizerData in [[stage_in]]) { return in.color; }
//每当视图需要渲染帧时调用
- (void)drawInMTKView:(nonnull MTKView *)view { static const AAPLVertex triangleVertices[] = { // 2D 顶点, RGBA 颜色值 { { 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"; MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor; //判断渲染目标是否为空 if(renderPassDescriptor != nil) { //创建渲染命令编码器,这样我们才可以渲染到something id<MTLRenderCommandEncoder> renderEncoder =[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; //渲染器名称 renderEncoder.label = @"MyRenderEncoder"; //区别于hello的 MTLViewport viewPort = { 0.0,0.0,_viewportSize.x,_viewportSize.y,-1.0,1.0 }; [renderEncoder setViewport:viewPort]; [renderEncoder setRenderPipelineState:_pipelineState]; [renderEncoder setVertexBytes:triangleVertices length:sizeof(triangleVertices) atIndex:AAPLVertexInputIndexVertices]; [renderEncoder setVertexBytes:&_viewportSize length:sizeof(_viewportSize) atIndex:AAPLVertexInputIndexViewportSize]; [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:3]; //表示已该编码器生成的命令都已完成,并且从NTLCommandBuffer中分离 [renderEncoder endEncoding]; //一旦框架缓冲区完成,使用当前可绘制的进度表 [commandBuffer presentDrawable:view.currentDrawable]; } //最后,在这里完成渲染并将命令缓冲区推送到GPU [commandBuffer commit]; }