Из этого туториала вы узнаете, как начать работу с Metal — платформой, представленной в iOS 8, которая поддерживает ускоренную визуализацию трехмерной графики на GPU и параллельные вычисления данных. В этом уроке мы рассмотрим теоретические концепции, лежащие в основе металла. Вы также узнаете, как создать приложение Metal, которое устанавливает необходимое аппаратное состояние для графики, фиксирует команды для выполнения в графическом процессоре и управляет буфером, объектами текстур и предварительно скомпилированными шейдерами.
1. Перво наперво
В этом руководстве предполагается, что вы знакомы с языком Objective-C и имеете некоторый опыт работы с OpenGL, OpenCL или схожим графическим API.
Также требуется физическое устройство с процессором Apple A7 или A8. Это означает, что вам понадобится iPhone 5S, 6 или 6 Plus или iPad Air или mini (2-го поколения). Симулятор iOS выдаст вам ошибки компиляции.
Этот урок сфокусирован только на металле и не охватывает язык затенения металла. Мы создадим шейдер, но мы рассмотрим только основные операции для взаимодействия с ним.
Если вы впервые используете XCode, убедитесь, что вы добавили свой Apple ID в разделе « Учетные записи » в настройках XCode . Это гарантирует, что вы не столкнетесь с проблемами при развертывании приложения на вашем устройстве.
Xcode 6 включает шаблон проекта для Metal, но чтобы помочь вам лучше понять Metal, мы собираемся создать проект с нуля.
В заключение отметим, что в этом учебном пособии мы будем использовать Objective-C, и важно, чтобы вы имели базовое понимание этого языка программирования.
2. Введение
Для тех из вас, кто знаком с OpenGL или OpenGL ES, Metal представляет собой низкоуровневую трехмерную графическую среду, но с меньшими накладными расходами. В отличие от каркасов Apple Sprite Kit или Scene Kit, с которыми вы по умолчанию не можете взаимодействовать с конвейером рендеринга, с Metal у вас есть абсолютная возможность создавать, контролировать и изменять этот конвейер.
Металл имеет следующие особенности:
- Инфраструктура обеспечивает чрезвычайно низкий доступ к графическим процессорам A7 и A8, обеспечивая невероятно высокую производительность для сложных графических процессов и вычислительных задач.
- Metal устраняет многие узкие места в производительности, такие как дорогостоящая проверка состояния, которая встречается в традиционных графических API.
- Он явно предназначен для перемещения всех дорогостоящих операций перевода и компиляции состояний из среды выполнения и рендеринга.
- Он предоставляет предварительно скомпилированные шейдеры, объекты состояния и явное планирование команд, чтобы гарантировать, что ваше приложение достигает максимально возможной производительности и эффективности для рендеринга графики и вычислительных задач.
- Каркас был разработан с учетом современных архитектурных соображений, таких как многопроцессорность и общая память.
- Он тесно интегрирован с iOS 8, чипсетами A7 и A8 и оборудованием Apple, создавая единую и независимую среду.
Достаточно теории, пришло время понять, как создается приложение Metal.
3. Создание металлического приложения
Металлическое приложение характеризуется набором необходимых шагов для правильного представления данных на экране. Эти шаги обычно создаются по порядку, и некоторые ссылки передаются от одного к другому. Эти шаги:
- получить устройство
- создать очередь команд
- создавать ресурсы, такие как буферы, текстуры и шейдеры
- создать конвейер рендеринга
- создать вид
Шаг 1: Получить устройство
Этот шаг включает в себя создание объекта MTLDevice
, сердце приложения Metal. Класс MTLDevice
обеспечивает прямой способ связи с драйвером и оборудованием GPU. Чтобы получить ссылку на экземпляр MTLDevice
, вам нужно вызвать Системное устройство по умолчанию, как показано ниже. С этой ссылкой у вас есть прямой доступ к оборудованию устройства.
1
|
id <MTLDevice> mtlDevice = MTLCreateSystemDefaultDevice();
|
Шаг 2. Создание очереди команд
Класс MTLCommandQueue
предоставляет способ отправки команд или инструкций в графический процессор. Чтобы инициализировать экземпляр класса MTLCommandQueue
, вам нужно использовать объект MTLDevice
мы создали ранее, и вызвать для newCommandQueue
метод newCommandQueue
.
1
|
id <MTLCommandQueue> mtlCommandQueue = [mtlDevice newCommandQueue];
|
Шаг 3: Создание ресурсов
Этот шаг включает в себя создание ваших буферных объектов, текстур и других ресурсов. В этом уроке вы создадите вершины. Эти объекты хранятся на стороне сервера / графического процессора, и для связи с ними необходимо создать определенную структуру данных, которая должна содержать данные, аналогичные тем, которые доступны в объекте вершины.
Например, если вам нужно передать данные для 2D-позиции вершины, вы должны объявить одну структуру данных, содержащую объект для этой 2D-позиции. Затем вы должны объявить это как в клиенте, в вашем приложении iOS, так и на стороне сервера, в шейдере Metal. Посмотрите на следующий пример для пояснения.
1
2
3
|
typedef struct {
GLKVector2 position;
}YourDataStruct;
|
Обратите внимание, что вам нужно импортировать библиотеку GLKMath из среды GLKit, как показано ниже.
1
|
#import <GLKit/GLKMath.h>
|
Затем вы объявляете объект с правильными координатами.
1
2
3
4
5
|
YourDataStruct triangle[3] = {
{ -.5f, 0.0f },
{ 0.5f, 0.0f },
{ 0.0f, 0.5f }
};
|
Шаг 4. Создайте конвейер рендеринга.
Создание конвейера рендеринга, вероятно, самый сложный шаг, поскольку вы должны позаботиться о нескольких инициализациях и конфигурациях, каждая из которых показана на следующей диаграмме.
Конвейер рендеринга настроен с использованием двух классов:
-
MTLRenderPipelineDescriptor
: предоставляет все ваши состояния конвейера рендеринга, такие как буферы положения вершины, цвета, глубины и трафарета, среди других -
MTLRenderPipelineState
: скомпилированная версияMTLRenderPipelineDescriptor
которая будет развернута на устройстве
Обратите внимание, что вам не нужно создавать все объекты конвейера рендеринга. Вы должны просто создать те, которые отвечают вашим потребностям.
В следующем фрагменте кода показано, как создать объект MTLRenderPipelineDescriptor
.
1
|
MTLRenderPipelineDescriptor *mtlRenderPipelineDescriptor = [MTLRenderPipelineDescriptor new];
|
На данный момент вы создали дескриптор, но вам все равно нужно настроить его как минимум в формате пикселей. Это то, что мы делаем в следующем блоке кода.
1
|
mtlRenderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
Для более сложных приложений вам также необходимо установить вершинный и фрагментный шейдеры по умолчанию, как показано ниже.
1
2
3
|
id <MTLLibrary> lib = [mtlDevice newDefaultLibrary];
mtlRenderPipelineDescriptor.vertexFunction = [lib newFunctionWithName:@»SomeVertexMethodName»];
mtlRenderPipelineDescriptor.fragmentFunction = [lib newFunctionWithName:@“SomeFragmentMethodName»];
|
Метод newFunctionWithName
ищет ваш исходный файл Metal в SomeVertexMethodName
метода SomeVertexMethodName
. Имя самого шейдера не важно, так как поиск выполняется непосредственно через имена методов. Это означает, что вы должны определить уникальные методы для уникальных операций шейдера. Позже мы подробнее рассмотрим металлические шейдеры.
После создания и настройки объекта MTLRenderPipelineDescriptor
следующим шагом является создание и определение MTLRenderPipelineState
путем передачи вновь созданного объекта MTLRenderPipelineDescriptor
.
1
2
|
NSError *error = [[NSError alloc] init];
id <MTLRenderPipelineState> mtlRenderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:mtlRenderPipelineDescriptor error:&error];
|
Шаг 5: Создать представление
Чтобы создать представление Metal, вам нужно UIView
подкласс UIView
и переопределить метод layerClass
как показано ниже.
1
2
3
|
+(id)layerClass{
return [CAMetalLayer class];
}
|
В этом руководстве мы рассмотрим другой способ создания класса CAMetalLayer
который дает разработчику больше контроля над характеристиками и конфигурацией уровня.
4. Рисуем металлическое приложение
Теперь, когда мы инициализировали необходимые объекты, нам нужно начать рисовать что-то на экране. Как и при инициализации, вам нужно выполнить ряд шагов:
- получить буфер команд
- установить проход рендеринга
- привлечь.
- передать в буфер команд
Шаг 1: получить командный буфер
Первым шагом является создание объекта, в котором хранится последовательный список команд, которые должно выполнить устройство. Вы создаете объект MTLCommandBuffer
и добавляете команды, которые будут последовательно выполняться графическим процессором. В следующем фрагменте кода показано, как создать буфер команд. Мы используем объект MTLCommandQueue
мы создали ранее.
1
|
id <MTLCommandBuffer> mtlCommandBuffer = [mtlCommandQueue commandBuffer];
|
Шаг 2: Начните проход рендеринга
В Metal конфигурация рендеринга является сложной, и вам необходимо явно указать, когда проход рендеринга начинается и когда он заканчивается. Вы должны определить конфигурации кадрового буфера заранее, чтобы iOS правильно сконфигурировала оборудование для этой конкретной конфигурации.
Для тех, кто знаком с OpenGL и OpenGL ES, этот шаг аналогичен, так как кадровый буфер имеет одинаковые свойства, конфигурации Color Attachment (от 0 до 3), Depth и Stencil . Вы можете увидеть визуальное представление этого шага на диаграмме ниже.
Сначала вам нужно создать текстуру для рендеринга. Текстура создается из класса CAMetalDrawable
и использует метод nextDrawable
для извлечения следующей текстуры для рисования в списке.
1
2
|
id <CAMetalDrawable> frameDrawable;
frameDrawable = [renderLayer nextDrawable];
|
Этот вызов nextDrawable
может и будет узким местом вашего приложения, поскольку он может легко заблокировать ваше приложение. Процессор и графический процессор могут быть десинхронизированы, и один должен ждать другого, что может вызвать оператор блока. Существуют синхронные механизмы, которые можно и нужно всегда реализовывать для решения этих проблем, но я не буду рассматривать их в этом вводном руководстве.
Теперь, когда у вас есть текстура для рендеринга, вам нужно создать объект MTLRenderPassDescriptor
для хранения кадрового буфера и информации о текстуре. Посмотрите на следующий фрагмент кода, чтобы увидеть, как это работает.
1
2
3
4
5
|
MTLRenderPassDescriptor *mtlRenderPassDescriptor;
mtlRenderPassDescriptor = [MTLRenderPassDescriptor new];
mtlRenderPassDescriptor.colorAttachments[0].texture = frameDrawable.texture;
mtlRenderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
mtlRenderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.75, 0.25, 1.0, 1.0);
|
Первый шаг устанавливает текстуру для рисования. Второе определяет конкретное действие, которое необходимо предпринять, в этом случае очистка текстуры и предотвращение загрузки содержимого этой текстуры в кэш GPU. Последний шаг изменяет цвет фона на определенный цвет.
Шаг 3: Ничья
С настроенным MTLRenderCommandEncoder
буфером и текстурой самое время создать экземпляр MTLRenderCommandEncoder
. Класс MTLRenderCommandEncoder
отвечает за традиционные взаимодействия с экраном и может рассматриваться как контейнер для состояния визуализации графики. Он также переводит ваш код в аппаратный формат команды, который будет выполняться устройством.
1
2
3
4
|
id <MTLRenderCommandEncoder> renderCommand = [mtlCommandBuffer renderCommandEncoderWithDescriptor: mtlRenderPassDescriptor];
// Set MTLRenderPipelineState
// Draw objects here
[renderCommand endEncoding];
|
Шаг 4: фиксация в командном буфере
Теперь у нас есть буфер и инструкции, ожидающие в памяти. Следующим шагом является фиксация команд в буфере команд и просмотр графики, отображаемой на экране. Обратите внимание, что графический процессор будет выполнять только тот код, который вы специально зафиксировали для эффекта. Следующие строки кода позволяют запланировать ваш кадровый буфер и зафиксировать буфер команд в GPU.
1
2
|
[mtlCommandBuffer presentDrawable:frameDrawable];
[mtlCommandBuffer commit];
|
На этом этапе у вас должно быть общее представление о том, как структурировано приложение Metal. Однако, чтобы правильно понять все это, нужно сделать это самостоятельно. Пришло время написать свое первое приложение Metal.
5. Создание металлического приложения
Запустите Xcode 6 и выберите New> Project … в меню File . Выберите Single View Application из списка шаблонов и выберите название продукта. Установите Objective-C в качестве языка и выберите iPhone в меню « Устройства» .
Откройте ViewController.m и добавьте следующие операторы импорта вверху.
1
2
|
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
|
Вам также необходимо добавить платформы Metal и QuartzCore в разделе « Связанные фреймворки и библиотеки » на этапах сборки цели. Отныне ваше внимание должно быть направлено на файл ViewController
класса ViewController
.
6. Создание структуры металла
Как я упоминал ранее, ваша первая задача — установить и инициализировать основные объекты, используемые во всем приложении. В следующем фрагменте кода мы объявляем несколько переменных экземпляра. Они должны выглядеть знакомо, если вы прочитали первую часть этого урока.
1
2
3
4
5
6
7
8
9
|
@implementation ViewController
{
id <MTLDevice> mtlDevice;
id <MTLCommandQueue> mtlCommandQueue;
MTLRenderPassDescriptor *mtlRenderPassDescriptor;
CAMetalLayer *metalLayer;
id <CAMetalDrawable> frameDrawable;
CADisplayLink *displayLink;
}
|
В методе viewDidLoad
контроллера viewDidLoad
мы инициализируем MTLDevice
и CommandQueue
.
1
2
|
mtlDevice = MTLCreateSystemDefaultDevice();
mtlCommandQueue = [mtlDevice newCommandQueue];
|
Теперь вы можете взаимодействовать с вашим устройством и создавать очереди команд. Теперь пришло время настроить объект CAMetalLayer
. Ваш слой CAMetalLayer
должен иметь определенную конфигурацию в зависимости от устройства, формата пикселя и размера кадра. Вы также должны указать, что он будет использовать только кадровый буфер и что он должен быть добавлен к текущему слою.
Если у вас возникли проблемы с настройкой объекта CAMetalLayer
, вам поможет следующий фрагмент кода.
1
2
3
4
5
6
|
metalLayer = [CAMetalLayer layer];
[metalLayer setDevice:mtlDevice];
[metalLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
metalLayer.framebufferOnly = YES;
[metalLayer setFrame:self.view.layer.frame];
[self.view.layer addSublayer:metalLayer];
|
Вы также должны установить непрозрачность вида, цвет фона и масштабный коэффициент содержимого. Это показано в следующем фрагменте кода.
1
2
3
|
[self.view setOpaque:YES];
[self.view setBackgroundColor:nil];
[self.view setContentScaleFactor:[UIScreen mainScreen].scale];
|
Единственный оставленный шаг — визуализация чего-либо на экране. Инициализируйте CADisplayLink
, передавая self
в качестве цели и @selector(renderScene)
в качестве селектора. Наконец, добавьте объект CADisplayLink
в текущий цикл выполнения.
1
2
|
displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderScene)];
[displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
|
Вот как должен выглядеть завершенный метод viewDidLoad
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
— (void)viewDidLoad {
[super viewDidLoad];
mtlDevice = MTLCreateSystemDefaultDevice();
mtlCommandQueue = [mtlDevice newCommandQueue];
metalLayer = [CAMetalLayer layer];
[metalLayer setDevice:mtlDevice];
[metalLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
metalLayer.framebufferOnly = YES;
[metalLayer setFrame:self.view.layer.frame];
[self.view.layer addSublayer:metalLayer];
[self.view setOpaque:YES];
[self.view setBackgroundColor:nil];
[self.view setContentScaleFactor:[UIScreen mainScreen].scale];
displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderScene)];
[displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
}
|
Если вы создадите проект, вы заметите, что Xcode дает нам одно предупреждение. Нам все еще нужно реализовать метод renderScene
.
Метод renderScene
выполняется каждый кадр. Есть несколько объектов, которые нужно инициализировать для каждого нового кадра, например, объекты MTLCommandBuffer
и MTLRenderCommandEncoder
.
Шаги, которые мы должны предпринять для рендеринга кадра:
- создать объект
MTLCommandBuffer
- инициализировать объект
CAMetalDrawable
- инициализировать объект
MTLRenderPassDescriptor
- настроить свойства
texture
,loadAction
,clearColor
иstoreAction
объектаMTLRenderPassDescriptor
- создать новый объект
MTLRenderCommandEncoder
- представить отрисовку и зафиксировать командный буфер
Не стесняйтесь пересмотреть то, что мы видели до сих пор, чтобы решить эту проблему самостоятельно. Если вы хотите продолжить работу с этим учебником, взгляните на решение, показанное ниже.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
— (void)renderScene
{
id <MTLCommandBuffer>mtlCommandBuffer = [mtlCommandQueue commandBuffer];
while (!frameDrawable){
frameDrawable = [metalLayer nextDrawable];
}
if (!mtlRenderPassDescriptor)
mtlRenderPassDescriptor = [MTLRenderPassDescriptor new];
mtlRenderPassDescriptor.colorAttachments[0].texture = frameDrawable.texture;
mtlRenderPassDescriptor.colorAttachments[0].loadAction = MTLLoadActionClear;
mtlRenderPassDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.75, 0.25, 1.0, 1.0);
mtlRenderPassDescriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
id <MTLRenderCommandEncoder> renderCommand = [mtlCommandBuffer renderCommandEncoderWithDescriptor: mtlRenderPassDescriptor];
// Draw objects here
// set MTLRenderPipelineState..
[renderCommand endEncoding];
[mtlCommandBuffer presentDrawable: frameDrawable];
[mtlCommandBuffer commit];
mtlRenderPassDescriptor = nil;
frameDrawable = nil;
}
|
Нам также необходимо реализовать метод dealloc
контроллера представления, в котором мы делаем недействительным объект displayLink
. Мы устанавливаем объекты mtlDevice
и mtlCommandQueue
в nil
.
1
2
3
4
5
6
|
-(void) dealloc
{
[displayLink invalidate];
mtlDevice = nil;
mtlCommandQueue = nil;
}
|
7. Рисование треугольника
Теперь у вас есть очень простое приложение по металлу. Пришло время добавить ваш первый графический примитив, треугольник. Первый шаг — создать структуру для треугольника.
1
2
3
|
typedef struct {
GLKVector2 position;
}Triangle;
|
Не забудьте добавить оператор импорта для библиотеки GLKMath вверху ViewController.m .
1
|
#import <GLKit/GLKMath.h>
|
Чтобы отобразить треугольник, необходимо создать объект MTLRenderPipelineState
объект MTLRenderPipelineState
. Кроме того, каждый нарисованный на экране объект принадлежит классу MTLBuffer
.
1
2
3
|
MTLRenderPipelineDescriptor *renderPipelineDescriptor;
id <MTLRenderPipelineState> renderPipelineState;
id <MTLBuffer> object;
|
После объявления этих переменных экземпляра вы должны инициализировать их в методе viewDidLoad
как я объяснил ранее.
1
2
|
renderPipelineDescriptor = [MTLRenderPipelineDescriptor new];
renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
|
Чтобы заштриховать треугольник, нам понадобятся металлические шейдеры. Металлические шейдеры должны быть назначены объекту MTLRenderPipelineDescriptor
и инкапсулированы с MTLLibrary
протокола MTLLibrary
. Это может показаться сложным, но вам нужно использовать только следующие строки кода:
1
2
3
4
|
id <MTLLibrary> lib = [mtlDevice newDefaultLibrary];
renderPipelineDescriptor.vertexFunction = [lib newFunctionWithName:@»VertexColor»];
renderPipelineDescriptor.fragmentFunction = [lib newFunctionWithName:@»FragmentColor»];
renderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error: nil];
|
Первая строка создает объект, соответствующий протоколу MTLLibrary
. Во второй строке мы сообщаем библиотеке, какой метод нужно вызывать внутри шейдера, чтобы управлять проходом вершин внутри конвейера рендеринга. В третьей строке мы повторяем этот шаг на уровне пикселей, фрагменты. Наконец, в последней строке мы создаем объект MTLRenderPipelineState
.
В Metal вы можете определить системные координаты, но в этом уроке вы будете использовать систему координат по умолчанию, то есть координаты центра экрана равны (0,0)
.
В следующем блоке кода мы создаем треугольный объект с тремя координатами (-.5f, 0.0f), (0.5f, 0.0f), (0.0f, 0.5f)
.
1
|
Triangle triangle[3] = { { -.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 0.5f } };
|
Затем мы добавляем треугольник к <MTLBuffer>
, который создает буфер для треугольника.
1
|
object = [mtlDevice newBufferWithBytes:&triangle length:sizeof(Triangle[3]) options:MTLResourceOptionCPUCacheModeDefault];
|
Вот как должен выглядеть завершенный метод viewDidLoad
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
— (void)viewDidLoad {
[super viewDidLoad];
mtlDevice = MTLCreateSystemDefaultDevice();
mtlCommandQueue = [mtlDevice newCommandQueue];
metalLayer = [CAMetalLayer layer];
[metalLayer setDevice:mtlDevice];
[metalLayer setPixelFormat:MTLPixelFormatBGRA8Unorm];
metalLayer.framebufferOnly = YES;
[metalLayer setFrame:self.view.layer.frame];
[self.view.layer addSublayer:metalLayer];
[self.view setOpaque:YES];
[self.view setBackgroundColor:nil];
[self.view setContentScaleFactor:[UIScreen mainScreen].scale];
// Create a reusable pipeline
renderPipelineDescriptor = [MTLRenderPipelineDescriptor new];
renderPipelineDescriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;
id <MTLLibrary> lib = [mtlDevice newDefaultLibrary];
renderPipelineDescriptor.vertexFunction = [lib newFunctionWithName:@»VertexColor»];
renderPipelineDescriptor.fragmentFunction = [lib newFunctionWithName:@»FragmentColor»];
renderPipelineState = [mtlDevice newRenderPipelineStateWithDescriptor:renderPipelineDescriptor error: nil];
Triangle triangle[3] = { { -.5f, 0.0f }, { 0.5f, 0.0f }, { 0.0f, 0.5f } };
object = [mtlDevice newBufferWithBytes:&triangle length:sizeof(Triangle[3]) options:MTLResourceOptionCPUCacheModeDefault];
displayLink = [CADisplayLink displayLinkWithTarget: self selector: @selector(renderScene)];
[displayLink addToRunLoop: [NSRunLoop currentRunLoop] forMode: NSDefaultRunLoopMode];
}
|
Метод viewDidLoad
завершен, но отсутствует последний шаг — создание шейдеров.
8. Создание шейдеров
Чтобы создать металлический шейдер, выберите « Создать»> «Файл …» в меню « Файл» , выберите « Источник» > « Металлический файл» в разделе iOS и назовите его « MyShader» . Xcode создаст для вас новый файл, MyShader.metal .
В верхней части вы должны увидеть следующие две строки кода. Первая включает стандартную библиотеку металлов, а вторая — metal
пространство имен.
1
2
|
#include <metal_stdlib>
using namespace metal;
|
Первым шагом является копирование структуры треугольника в шейдер. Шейдеры обычно делятся на две разные операции: вершина и пиксель (фрагменты). Первая связана с положением вершины, а вторая связана с конечным цветом этой вершины и всеми позициями пикселей внутри многоугольника. Вы можете посмотреть на это так: первый растеризует многоугольник, пиксели многоугольника, а второй заштрихует те же пиксели.
Поскольку они должны общаться в однонаправленном режиме, от вершины к фрагменту, лучше всего создать структуру для данных, которые будут переданы. В этом случае мы только передаем позицию.
1
2
3
|
typedef struct {
float4 position [[position]];
} TriangleOutput;
|
Теперь давайте создадим методы вершин и фрагментов. Помните, когда вы программировали объект RenderPipelineDescriptor
для вершины и фрагмента? Вы использовали метод newFunctionWithName
, передавая объект NSString
. Эта строка является именем метода, который вы вызываете внутри шейдера. Это означает, что вам нужно объявить два метода с этими именами, VertexColor
и FragmentColor
.
Что это значит? Вы можете создавать свои шейдеры и называть их так, как вам нравится, но вам нужно вызывать методы точно так, как вы их объявили, и они должны иметь уникальные имена.
Внутри ваших шейдеров добавьте следующий блок кода.
01
02
03
04
05
06
07
08
09
10
11
|
vertex TriangleOutput VertexColor(const device Triangle *Vertices [[buffer(0)]], const uint index [[vertex_id]])
{
TriangleOutput out;
out.position = float4(Vertices[index].position, 0.0, 1.0);
return out;
}
fragment half4 FragmentColor(void)
{
return half4(1.0, 0.0, 0.0, 1.0);
}
|
Метод VertexColor
будет получать данные, хранящиеся в позиции 0
буфера (выделенная память) и vertex_id
вершины. Поскольку мы объявили vertex_id
треугольник, vertex_id
будет 0
, 1
и 2
. Он выводит объект TriangleOutput
который автоматически принимается FragmentColor
. Наконец, он затеняет каждый пиксель внутри этих трех вершин, используя красный цвет.
Вот и все. Создайте и запустите приложение и наслаждайтесь первым, совершенно новым приложением 60fps Metal.
9. Внешние ресурсы
Если вы хотите узнать больше о платформе Metal и о том, как она работает, вы можете проверить несколько других ресурсов:
- WWDC 2014 (Работа с металлическими секциями)
- Металлический каркас
- Руководство по программированию металлов
Вывод
На этом мы завершаем наше вводное руководство по новой металлической структуре. Если у вас есть какие-либо вопросы или комментарии, не стесняйтесь оставлять комментарии в комментариях.