Metal渲染加載顏色、三角形

1身诺、渲染加載顏色

顏色蜜托、三角形案例demo

未命名.gif

渲染加載顏色

導入MetalKit工具包,@import MetalKit;
我們接受蘋果的建議分離渲染循環(huán)霉赡。另建文件CCRender橄务,遵循MTKViewDelegate協(xié)議。
在Controller中創(chuàng)建需要的MTKView穴亏、CCRender對象

  • MTKView對象,設置metal設備(GPU)蜂挪。及渲染循環(huán)CCrender對象。
//創(chuàng)建MTKView嗓化。設置代理為負責渲染的CCRender
 _mtkView = [[MTKView alloc]initWithFrame:CGRectMake(10, 10, self.view.frame.size.width-20, self.view.frame.size.height-20) device:MTLCreateSystemDefaultDevice()];
 [self.view addSubview:_mtkView];
  • 創(chuàng)建負責渲染CCRender
//創(chuàng)建負責渲染的CCRender棠涮,
_render = [[CCRender alloc]initWithMetalKitView:_mtkView];
  • 設置MTKView的代理為負責渲染CCRender
_mtkView.delegate = _render;
  • 設置幀速率
    幀速率越大,每秒處理的幀數(shù)越多刺覆,變化越快严肪。
    也可以不設置。使用默認幀速率值
_mtkView.preferredFramesPerSecond = 100;

CCRender 渲染循環(huán)類

  • 1谦屑、設置CCRender當前的deviceMTKViewdevice
_device = mtkView.device;
  • 2驳糯、從當前設備device(GPU)創(chuàng)建命令隊列MTLCommandQueue
_commandQueue = [_device newCommandQueue];
  • 3、執(zhí)行渲染的代理方法-(void)drawInMTKView:(nonnull MTKView *)view每當視圖進行渲染是調(diào)用伦仍。
  • 3.1结窘、獲取變化的顏色、設置清屏顏色
    //獲取顏色
    Color color = [self makeFancyColor];
    //設置清屏顏色
    view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha);
  • 3.2充蓝、為當前渲染創(chuàng)建新的命令緩沖區(qū)
    創(chuàng)建渲染緩存區(qū)隧枫,目的是為了將渲染對象加入到渲染緩存區(qū),使用MTLCommandQueue 創(chuàng)建對象并且加入到MTCommandBuffer對象中谓苟,且為當前渲染的每個渲染傳遞創(chuàng)建一個新的命令緩沖區(qū)官脓。
   //為當前渲染的每個渲染傳遞創(chuàng)建一個新的命令緩沖區(qū)
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    //指定緩存區(qū)名稱
    commandBuffer.label = @"MyCommand";
  • 3.3、從當前視圖獲取渲染描述符MTLRenderPassDescriptor涝焙,用于創(chuàng)建命令編譯器MTLRenderCommandEncoder卑笨。
//獲取渲染描述符
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
//創(chuàng)建命令編譯器
if(renderPassDescriptor != nil)
    {
        //4.創(chuàng)建渲染命令編碼器,這樣我們才可以渲染到something
        id<MTLRenderCommandEncoder> renderEncoder =[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        //渲染器名稱
        renderEncoder.label = @"MyRenderEncoder";
        [renderEncoder endEncoding];
        /*
         當編碼器結束之后,命令緩存區(qū)就會接受到2個命令.
         1) present
         2) commit
         因為GPU是不會直接繪制到屏幕上,因此你不給出去指令.是不會有任何內(nèi)容渲染到屏幕上.
        */
        //添加一個最后的命令來顯示清除的可繪制的屏幕
        [commandBuffer presentDrawable:view.currentDrawable];
    }
  • 3.4、結束渲染仑撞。將命令緩沖區(qū)提交GPU
[commandBuffer commit];
  • 設置顏色變換
//顏色結構體
typedef struct {
    float red,green,blue,alpha;
}Color;

-(Color)makeFancyColor{
    //增加顏色 赤兴、減少顏色標記
    static BOOL growing = YES;
    //顏色通道(0~3)
    static NSUInteger primaryChannel = 0;
    //3.顏色通道數(shù)組colorChannels(顏色值)
    static float      colorChannels[] = {1.0, 0.0, 0.0, 1.0};
    //4.顏色調(diào)整步長
    const float DynamicColorRate = 0.015;
    if (growing) {
        //動態(tài)信道索引(1妖滔,2,3桶良,0)通道間的切換
        NSInteger dynamicChannelIndex = (primaryChannel + 1) % 3;
        //修改對應通道的顏色值 調(diào)整0.015
        colorChannels[dynamicChannelIndex] += DynamicColorRate;
        //當顏色通道值 = 1.0時
        if (colorChannels[dynamicChannelIndex] >= 1.0) {
            //設置為no
            growing = NO;
            //將顏色通道修改為動態(tài)顏色通道
            primaryChannel = dynamicChannelIndex;
        }
    }else{
        //獲取動態(tài)顏色通道
        NSUInteger dynamicChannelIndex = (primaryChannel+2)%3;
        
        //將當前顏色的值 減去0.015
        colorChannels[dynamicChannelIndex] -= DynamicColorRate;
        
        //當顏色值小于等于0.0
        if(colorChannels[dynamicChannelIndex] <= 0.0)
        {
            //又調(diào)整為顏色增加
            growing = YES;
        }
    }
    //創(chuàng)建顏色
    Color color;
    
    //修改顏色的RGBA的值
    color.red   = colorChannels[0];
    color.green = colorChannels[1];
    color.blue  = colorChannels[2];
    color.alpha = colorChannels[3];
    
    //返回顏色
    return color;
}

2座舍、繪制三角形

三角形
繪制三角形

創(chuàng)建Metal著色器文件
定義頂點著色器的輸入、片元著色器的輸出

// 頂點著色器輸出和片段著色器輸入
//結構體
typedef struct
{
    //處理空間的頂點信息
    float4 clipSpacePosition [[position]];
    //顏色
    float4 color;
} RasterizerData;

頂點著色器函數(shù)陨帆、片元著色器函數(shù)

/*
 * vertex:修飾符曲秉,表示是頂點著色器
 * RasterizerData:返回值
 * vertexShader:函數(shù)名稱,自定義
 * vertexID:當前所處理的頂點號疲牵。metal自己反饋的id 
 * vertices:1承二、告訴存儲的位置buffer。 2纲爸、告訴傳遞數(shù)據(jù)的入口是CCVertexInputIndexVertices
 * viewportSizePointer:視口
 * vertices 和 viewportSizePointer 都是通過CCRender傳遞進來的
 */

//頂點著色函數(shù)
vertex RasterizerData
vertexShader(uint vertexID [[vertex_id]],
             constant CCVertex *vertices [[buffer(CCVertexInputIndexVertices)]],
             constant vector_uint2 *viewportSizePointer [[buffer(CCVertexInputIndexViewportSize)]])
{
    //定義返回值
   RasterizerData out; 
   //當前所處理的頂點
   out.clipSpacePosition = vertices[vertexID].position;

  //當前處理的頂點的顏色值亥鸠。橋接片元著色器
  //把我們輸入的顏色直接賦值給輸出顏色.
  out.color = vertices[vertexID].color;

  //完成! 將結構體傳遞到管道中下一個階段:
   return out;
}

/* fragment:修飾符,表示片元著色器
 * float4:返回值缩焦,顏色值RBGA
 * fragmentShader:函數(shù)名稱读虏,自定義
 * RasterizerData:參數(shù)類型
 * in:函數(shù)中形參變量(可修改)
 * [[stage_in]]:表示單個片元輸入(由頂點函數(shù)輸出,然后經(jīng)過光柵化生成)(不可修改)袁滥,相當于OpenGL ES中的fragment中的varying與頂點著色器中橋接的對應
*/

fragment float4 fragmentShader(RasterizerData in [[stage_in]])
{
    //返回輸入的片元顏色
    return in.color;
}

創(chuàng)建橋接文件
創(chuàng)建此文件為了Metal shadersC/OBJC 源之間共享的類型和枚舉常數(shù)。
緩存區(qū)索引值 共享與shaderC 代碼 為了確保Metal Shader緩存區(qū)索引能夠匹配 Metal API Buffer設置的集合調(diào)用

//緩存區(qū)索引值灾螃,表示向metal著色器傳遞數(shù)據(jù)的入口枚舉值
typedef enum CCVertexInputIndex
{
    //頂點
    CCVertexInputIndexVertices     = 0,
    //視圖大小
    CCVertexInputIndexViewportSize = 1,
} CCVertexInputIndex;

//圖形數(shù)據(jù)的結構體: 頂點/顏色值
typedef struct
{
    // 像素空間的位置
    // 像素中心點(100,100)
    vector_float4 position;

    // RGBA顏色
    vector_float4 color;
} CCVertex;

Controller初始化
創(chuàng)建MTKView對象設置設備device(GPU)题翻,
創(chuàng)建CCRender對象,設置MTKView的代理

//創(chuàng)建mtkView
 _mtkView = [[MTKView alloc]initWithFrame:CGRectMake(10, 10, self.view.frame.size.width-20, self.view.frame.size.height-20) device:MTLCreateSystemDefaultDevice()];
[self.view addSubview:_mtkView];

 if (!_mtkView.device) {
      NSLog(@"Metal is not supported on this device");
      return;
  }
_triangleRender = [[TriangleRender alloc]initMetalMtkView:_mtkView];
 _mtkView.delegate = _triangleRender; 
[_triangleRender mtkView:_mtkView drawableSizeWillChange:_mtkView.drawableSize];

負責渲染的CCRender

  • 設置當前設備device腰鬼、創(chuàng)建命令隊列MTLCommandQueue
//1.獲取GPU 設備
 _device = mtkView.device;
//5.創(chuàng)建命令隊列
 _commandQueue = [_device newCommandQueue];
  • 加載(.metal)著色器文件
 //2.在項目中加載所有的(.metal)著色器文件
 // 從bundle中獲取.metal文件
id<MTLLibrary> defaultLibrary = [_device newDefaultLibrary];
 //從庫中加載頂點函數(shù)
id<MTLFunction> vertexFunction = [defaultLibrary newFunctionWithName:@"vertexShader"]; 
//從庫中加載片元函數(shù)
id<MTLFunction> fragmentFunction = [defaultLibrary newFunctionWithName:@"fragmentShader"];
  • 配置渲染管道MTLRenderPipelineDescriptor
//3.配置用于創(chuàng)建管道狀態(tài)的管道
MTLRenderPipelineDescriptor *pipelineStateDescriptor = [[MTLRenderPipelineDescriptor alloc] init];
//管道名稱
pipelineStateDescriptor.label = @"Simple Pipeline";
//可編程函數(shù),用于處理渲染過程中的各個頂點
pipelineStateDescriptor.vertexFunction = vertexFunction;
//可編程函數(shù),用于處理渲染過程中各個片段/片元
pipelineStateDescriptor.fragmentFunction = fragmentFunction;
//一組存儲顏色數(shù)據(jù)的組件  
pipelineStateDescriptor.colorAttachments[0].pixelFormat = mtkView.colorPixelFormat;
  • 創(chuàng)建渲染管道對象MTLRenderPipelineDescriptor
    從當前device(GPU)創(chuàng)建配置好的渲染管道對象
_pipelineState = [_device newRenderPipelineStateWithDescriptor:pipelineStateDescriptor error:&error];
  • 視圖變化的代理方法
//當前視圖大小,
vector_uint2 _viewportSize;

//每當視圖改變方向或調(diào)整大小時調(diào)用
- (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size
{
    // 保存可繪制的大小嵌赠,因為當我們繪制時,我們將把這些值傳遞給頂點著色器
    _viewportSize.x = size.width;
    _viewportSize.y = size.height;
}
  • 渲染代理方法
//每當視圖需要渲染幀時調(diào)用
- (void)drawInMTKView:(nonnull MTKView *)view
{
    //1. 頂點數(shù)據(jù)/顏色數(shù)據(jù)
    static const CCVertex 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.為當前渲染的每個渲染傳遞創(chuàng)建一個新的命令緩沖區(qū)
    id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
    //指定緩存區(qū)名稱
    commandBuffer.label = @"MyCommand";
    

    // MTLRenderPassDescriptor:一組渲染目標熄赡,用作渲染通道生成的像素的輸出目標姜挺。
    MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
    //判斷渲染目標是否為空
    if(renderPassDescriptor != nil)
    {
        //4.創(chuàng)建渲染命令編碼器,這樣我們才可以渲染到something
        id<MTLRenderCommandEncoder> renderEncoder =[commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
        //渲染器名稱
        renderEncoder.label = @"MyRenderEncoder";

        //5.設置我們繪制的可繪制區(qū)域
        /*
        typedef struct {
            double originX, originY, width, height, znear, zfar;
        } MTLViewport;
         */
        //視口指定Metal渲染內(nèi)容的drawable區(qū)域。 視口是具有x和y偏移彼硫,寬度和高度以及近和遠平面的3D區(qū)域
        //為管道分配自定義視口需要通過調(diào)用setViewport:方法將MTLViewport結構編碼為渲染命令編碼器炊豪。 如果未指定視口,Metal會設置一個默認視口拧篮,其大小與用于創(chuàng)建渲染命令編碼器的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.設置當前渲染管道狀態(tài)對象
        [renderEncoder setRenderPipelineState:_pipelineState];
    
        
        //7.從應用程序OC 代碼 中發(fā)送數(shù)據(jù)給Metal 頂點著色器 函數(shù)
        //頂點數(shù)據(jù)+顏色數(shù)據(jù)
        //   1) 指向要傳遞給著色器的內(nèi)存的指針
        //   2) 我們想要傳遞的數(shù)據(jù)的內(nèi)存大小
        //   3)一個整數(shù)索引,它對應于我們的“vertexShader”函數(shù)中的緩沖區(qū)屬性限定符的索引串绩。

        [renderEncoder setVertexBytes:triangleVertices
                               length:sizeof(triangleVertices)
                              atIndex:CCVertexInputIndexVertices];

        //viewPortSize 數(shù)據(jù)
        //1) 發(fā)送到頂點著色函數(shù)中,視圖大小
        //2) 視圖大小內(nèi)存空間大小
        //3) 對應的索引
        [renderEncoder setVertexBytes:&_viewportSize
                               length:sizeof(_viewportSize)
                              atIndex:CCVertexInputIndexViewportSize];

       
        
        //8.畫出三角形的3個頂點
        // @method drawPrimitives:vertexStart:vertexCount:
        //@brief 在不使用索引列表的情況下,繪制圖元
        //@param 繪制圖形組裝的基元類型
        //@param 從哪個位置數(shù)據(jù)開始繪制,一般為0
        //@param 每個圖元的頂點個數(shù),繪制的圖型頂點數(shù)量
        /*
         MTLPrimitiveTypePoint = 0, 點
         MTLPrimitiveTypeLine = 1, 線段
         MTLPrimitiveTypeLineStrip = 2, 線環(huán)
         MTLPrimitiveTypeTriangle = 3,  三角形
         MTLPrimitiveTypeTriangleStrip = 4, 三角型扇
         */
    
        [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle
                          vertexStart:0
                          vertexCount:3];

        //9.表示已該編碼器生成的命令都已完成,并且從NTLCommandBuffer中分離
        [renderEncoder endEncoding];

        //10.一旦框架緩沖區(qū)完成缺虐,使用當前可繪制的進度表
        [commandBuffer presentDrawable:view.currentDrawable];
    }

    //11.最后,在這里完成渲染并將命令緩沖區(qū)推送到GPU
    [commandBuffer commit];
}
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市礁凡,隨后出現(xiàn)的幾起案子高氮,更是在濱河造成了極大的恐慌慧妄,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剪芍,死亡現(xiàn)場離奇詭異腰涧,居然都是意外死亡,警方通過查閱死者的電腦和手機紊浩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門窖铡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坊谁,你說我怎么就攤上這事费彼。” “怎么了口芍?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵箍铲,是天一觀的道長。 經(jīng)常有香客問我鬓椭,道長颠猴,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任小染,我火速辦了婚禮翘瓮,結果婚禮上,老公的妹妹穿的比我還像新娘裤翩。我一直安慰自己资盅,他們只是感情好,可當我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布踊赠。 她就那樣靜靜地躺著呵扛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筐带。 梳的紋絲不亂的頭發(fā)上今穿,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音伦籍,去河邊找鬼蓝晒。 笑死,一個胖子當著我的面吹牛鸽斟,可吹牛的內(nèi)容都是我干的拔创。 我是一名探鬼主播,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼富蓄,長吁一口氣:“原來是場噩夢啊……” “哼剩燥!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤灭红,失蹤者是張志新(化名)和其女友劉穎侣滩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體变擒,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡君珠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了娇斑。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片策添。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毫缆,靈堂內(nèi)的尸體忽然破棺而出唯竹,到底是詐尸還是另有隱情,我是刑警寧澤苦丁,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布浸颓,位于F島的核電站,受9級特大地震影響旺拉,放射性物質發(fā)生泄漏产上。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一蛾狗、第九天 我趴在偏房一處隱蔽的房頂上張望晋涣。 院中可真熱鬧,春花似錦淘太、人聲如沸姻僧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赌莺,卻和暖如春冰抢,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背艘狭。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工挎扰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人巢音。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓遵倦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親官撼。 傳聞我的和親對象是個殘疾皇子梧躺,可洞房花燭夜當晚...
    茶點故事閱讀 44,611評論 2 353