391 lines
41 KiB
HTML
391 lines
41 KiB
HTML
<!DOCTYPE html>
|
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-us" lang="en-us">
|
|
<head>
|
|
<link href="//gmpg.org/xfn/11" rel="profile">
|
|
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
|
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
|
<meta name="generator" content="Hugo 0.72.0" />
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
<title>CocoaPods Hello Triangle · Filament</title>
|
|
|
|
|
|
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/print.css" media="print">
|
|
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/poole.css">
|
|
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/syntax.css">
|
|
<link type="text/css" rel="stylesheet" href="https://google.github.io/filament/css/hyde.css">
|
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700|Tangerine:700|Inconsolata" rel="stylesheet">
|
|
|
|
|
|
|
|
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="/apple-touch-icon-144-precomposed.png">
|
|
<link rel="shortcut icon" href="/favicon.png">
|
|
|
|
|
|
<link href="" rel="alternate" type="application/rss+xml" title="Filament" />
|
|
|
|
|
|
</head>
|
|
|
|
<body class="theme-base-0d ">
|
|
<aside class="sidebar">
|
|
<div class="container sidebar-sticky">
|
|
<div class="sidebar-about">
|
|
<a href="https://google.github.io/filament/">
|
|
|
|
<div style="width:100%; height:150px">
|
|
<img src="https://google.github.io/filament/images/filament_logo_small.png" class="lightbulb" />
|
|
</div>
|
|
|
|
</a>
|
|
<p class="lead">
|
|
|
|
</p>
|
|
</div>
|
|
<nav>
|
|
<ul class="sidebar-nav">
|
|
<li>
|
|
<a href="https://github.com/google/filament"> GitHub Project </a>
|
|
|
|
</li><li>
|
|
<a href="/filament/documentation/"> Core Documentation </a>
|
|
|
|
</li><li>
|
|
<a href="/filament/ios/"> iOS Tutorials </a>
|
|
|
|
</li><li>
|
|
<a href="/filament/webgl/"> Web Docs / Demos </a>
|
|
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="content container">
|
|
<div class="post">
|
|
<h1>CocoaPods Hello Triangle</h1>
|
|
<p>As of release 1.8.0, you can install Filament in your iOS application using CocoaPods.</p>
|
|
<p>This guide will walk you through creating a basic “hello triangle” iOS application using Filament and the Metal backend.</p>
|
|
<p><img src="rotating-triangle.gif" alt="a rotating triangle"></p>
|
|
<p>The full source for this example is <a href="https://github.com/google/filament/tree/main/ios/samples/HelloCocoaPods">here</a>. If you’re just looking to get something up and running quickly, download the project, <code>pod install</code>, build, and run.</p>
|
|
<p>We’ll be walking through 7 steps to get the rotating triangle up and running. All of the code we’ll be writing will be in a single ViewController.mm file, and you can follow along <a href="https://github.com/google/filament/blob/main/ios/samples/HelloCocoaPods/HelloCocoaPods/ViewController.mm">here</a>.</p>
|
|
<ul>
|
|
<li><a href="#creating-a-boilerplate-app-with-filament">1. Creating a Boilerplate App</a></li>
|
|
<li><a href="#instantiating-the-filament-engine">2. Instantiating Filament</a></li>
|
|
<li><a href="#creating-a-swapchain">3. Creating a SwapChain</a></li>
|
|
<li><a href="#clearing-the-screen">4. Clearing the Screen</a></li>
|
|
<li><a href="#drawing-a-triangle">5. Drawing a Triangle</a></li>
|
|
<li><a href="#compiling-a-custom-material">6. Compiling a Custom Material</a></li>
|
|
<li><a href="#animating-the-triangle">7. Animating the Triangle</a></li>
|
|
</ul>
|
|
<h2 id="creating-a-boilerplate-app-with-filament">Creating a Boilerplate App with Filament</h2>
|
|
<p>We’ll start fresh by creating a new Single View App in Xcode.</p>
|
|
<p><img src="single-view-app.png" alt="create a single view app in Xcodde"></p>
|
|
<p>Give your app a name, and use the default options.</p>
|
|
<p><img src="default-options.png" alt="use the default options in Xcode"></p>
|
|
<p>If you haven’t used CocoaPods before, I recommend watching <a href="https://www.youtube.com/watch?v=iEAjvNRdZa0">this Route 85 video</a> to help you get set up.</p>
|
|
<p>Create a Podfile in the Xcode project directory with the following:</p>
|
|
<pre><code>platform :ios, '11.0'
|
|
|
|
target 'HelloCocoaPods' do
|
|
pod 'Filament'
|
|
end
|
|
</code></pre><p>Then run:</p>
|
|
<pre><code>$ pod install
|
|
</code></pre><p>Close the project and then re-open the newly created HelloCocoaPods.xcworkspace file.</p>
|
|
<h2 id="instantiating-the-filament-engine">Instantiating the Filament Engine</h2>
|
|
<p>Before we do anything with Filament, we first need to include the appropriate headers. Filament exposes a C++ API, so any files that include Filament headers need to be compiled in a variant of C++. We’ll be using Objective-C++.</p>
|
|
<p>You should be able to simply change the extension of the default ViewController from .m to .mm, though I’ve found Xcode to be buggy with this on occasion. To make sure Xcode recognizes it as an Objective-C++ file, check that the type of file is “Objective-C++ Source”.</p>
|
|
<p><img src="obj-cpp.png" alt="change the type of ViewController.m to Objective-C++"></p>
|
|
<p>Then, add the following to the top of ViewController.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/Engine.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e"></span>
|
|
using namespace filament;
|
|
</code></pre></div><p>We’ll need to keep track of a few Filament objects, so let’s add a section for private instance variables and add a pointer for our <code>Engine</code> instance.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#66d9ef">@implementation</span> <span style="color:#a6e22e">Viewcontroller</span> {
|
|
Engine<span style="color:#f92672">*</span> _engine;
|
|
}
|
|
</code></pre></div><p>The Filament <code>Engine</code> is our main entrypoint into Filament. We start by instantiating it inside <code>viewDidLoad</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">viewDidLoad</span> {
|
|
[super viewDidLoad];
|
|
|
|
_engine <span style="color:#f92672">=</span> Engine<span style="color:#f92672">::</span>create(Engine<span style="color:#f92672">::</span>Backend<span style="color:#f92672">::</span>METAL);
|
|
}
|
|
</code></pre></div><p>We specify <code>Engine::Backend::METAL</code> to select the Metal backend. Filament also supports OpenGL on iOS, but we strongly recommend sticking to Metal.</p>
|
|
<p>Every Filament object we create must also be destroyed. Add the <code>dealloc</code> method and the following:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">dealloc</span> {
|
|
_engine<span style="color:#f92672">-></span>destroy(<span style="color:#f92672">&</span>_engine);
|
|
}
|
|
</code></pre></div><p>If you compile and run the app now you should see output similar to the following:</p>
|
|
<pre><code>FEngine (64 bits) created at 0x10ab94000 (threading is enabled)
|
|
FEngine resolved backend: Metal
|
|
</code></pre><h2 id="creating-a-swapchain">Creating a SwapChain</h2>
|
|
<p>Before we can render anything, we’ll first need to create a <code>SwapChain</code>. The <code>SwapChain</code> represents a platform-specific surface that can be rendered into. On iOS with Metal, it’s a <a href="https://developer.apple.com/documentation/quartzcore/cametallayer"><code>CAMetalLayer</code></a>.</p>
|
|
<p>We could set up our own <code>CAMetalLayer</code> if we wanted to, but Apple provides a <code>MTKView</code> that is already backed by a <code>CAMetalLayer</code>. It also has a delegate protocol with some methods that will make things easier for us.</p>
|
|
<p>Inside Main.storyboard, change the type of ViewController’s view to a <code>MTKView</code>.</p>
|
|
<p><img src="view.png" alt="ViewController view"></p>
|
|
<p><img src="mtkview.gif" alt="change type of MTKView"></p>
|
|
<p>Include the SwapChain.h and MTKView.h headers and make the <code>ViewController</code> conform to the <code>MTKViewDelegate</code> protocol.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/SwapChain.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#75715e">#import <MetalKit/MTKView.h>
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#66d9ef">@interface</span> <span style="color:#a6e22e">ViewController</span> () <span style="color:#f92672"><</span>MTKViewDelegate<span style="color:#f92672">></span>
|
|
|
|
<span style="color:#66d9ef">@end</span>
|
|
</code></pre></div><p>Add a new private var:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">SwapChain<span style="color:#f92672">*</span> _swapChain;
|
|
</code></pre></div><p>Inside <code>viewDidLoad</code>, we’ll set our <code>ViewController</code> as the <code>MTKView</code> delegate and instantiate our <code>SwapChain</code>. To instantiate the <code>SwapChain</code>, we pass in <code>view.layer</code> which, because we set our <code>View</code> to a <code>MTKView</code>, will be a <code>CAMetalLayer</code>. Filament’s API is platform-agnostic, which is why we need to cast the layer to a <code>void*</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">MTKView<span style="color:#f92672">*</span> mtkView <span style="color:#f92672">=</span> (MTKView<span style="color:#f92672">*</span>) self.view;
|
|
mtkView.delegate <span style="color:#f92672">=</span> self;
|
|
_swapChain <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-></span>createSwapChain((<span style="color:#66d9ef">__bridge</span> <span style="color:#66d9ef">void</span><span style="color:#f92672">*</span>) mtkView.layer);
|
|
</code></pre></div><p>The <code>SwapChain</code> needs to be destroyed in our <code>dealloc</code> function. We’ll destroy the objects in the reverse order we created them; the <code>Engine</code> object should always be the the last object we destroy.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-></span>destroy(_swapChain);
|
|
_engine<span style="color:#f92672">-></span>destroy(<span style="color:#f92672">&</span>_engine);
|
|
</code></pre></div><p>Finally, add stubs for some <code>MTKViewDelegate</code> methods, which we’ll fill in later.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">mtkView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view <span style="color:#a6e22e">drawableSizeWillChange:</span>(CGSize)size {
|
|
<span style="color:#75715e">// todo
|
|
</span><span style="color:#75715e"></span>}
|
|
|
|
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">drawInMTKView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view {
|
|
<span style="color:#75715e">// todo
|
|
</span><span style="color:#75715e"></span>}
|
|
</code></pre></div><h2 id="clearing-the-screen">Clearing The Screen</h2>
|
|
<p>We now have a Filament <code>Engine</code> and <code>SwapChain</code> set up. We’ll need a few more objects before we can render anything.</p>
|
|
<p>A Filament <code>Renderer</code> gives us an API to render frames into the <code>SwapChain</code>. It takes a <code>View</code>, which defines a <code>Viewport</code>, <code>Scene</code> and <code>Camera</code> for rendering. The <code>Camera</code> represents a vantage point into a <code>Scene</code>, which contains references to all the entities we want to render.</p>
|
|
<p>Creating these are objects is straightforward. First, include the appropriate headers</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c++" data-lang="c++"><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/Renderer.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/View.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/Camera.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/Scene.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/Viewport.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#75715e">#include</span> <span style="color:#75715e"><utils/Entity.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><utils/EntityManager.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#66d9ef">using</span> <span style="color:#66d9ef">namespace</span> utils;
|
|
</code></pre></div><p>add the following private vars</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">Renderer<span style="color:#f92672">*</span> _renderer;
|
|
View<span style="color:#f92672">*</span> _view;
|
|
Scene<span style="color:#f92672">*</span> _scene;
|
|
Camera<span style="color:#f92672">*</span> _camera;
|
|
Entity _cameraEntity;
|
|
</code></pre></div><p>and then instantiate them</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_renderer <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-></span>createRenderer();
|
|
_view <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-></span>createView();
|
|
_scene <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-></span>createScene();
|
|
</code></pre></div><p>The camera is a bit special. Filament uses an entity-component system, so we’ll first need to create an <code>Entity</code> which we then attach a <code>Camera</code> component to.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_cameraEntity <span style="color:#f92672">=</span> EntityManager<span style="color:#f92672">::</span>get().create();
|
|
_camera <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-></span>createCamera(_cameraEntity);
|
|
</code></pre></div><p>Let’s also inform our <code>Renderer</code> to clear to a light blue clear color, so we can know everything is working.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_renderer<span style="color:#f92672">-></span>setClearOptions({
|
|
.clearColor <span style="color:#f92672">=</span> {<span style="color:#ae81ff">0.25f</span>, <span style="color:#ae81ff">0.5f</span>, <span style="color:#ae81ff">1.0f</span>, <span style="color:#ae81ff">1.0f</span>},
|
|
.clear <span style="color:#f92672">=</span> true
|
|
});
|
|
</code></pre></div><p>The <code>Camera</code> and <code>Scene</code> need to be wired up to the <code>View</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_view<span style="color:#f92672">-></span>setScene(_scene);
|
|
_view<span style="color:#f92672">-></span>setCamera(_camera);
|
|
</code></pre></div><p>Our newly created objects get cleaned up inside <code>dealloc</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-></span>destroyCameraComponent(_cameraEntity);
|
|
EntityManager<span style="color:#f92672">::</span>get().destroy(_cameraEntity);
|
|
_engine<span style="color:#f92672">-></span>destroy(_scene);
|
|
_engine<span style="color:#f92672">-></span>destroy(_view);
|
|
_engine<span style="color:#f92672">-></span>destroy(_renderer);
|
|
</code></pre></div><p>We need to set the <code>Viewport</code> on our <code>View</code>, which we want to do whenever the size of our <code>SwapChain</code> changes. We’ll also update the projection matrix on our camera.</p>
|
|
<p>Let’s create a new method, <code>resize:</code>, which will update the <code>Viewport</code> on our <code>View</code> to a given size. We’ll call it in the <code>mtkView:drawableSizeWillChange:</code> delegate method, and at the end of <code>viewDidLoad</code>:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">resize:</span>(CGSize)size {
|
|
_view<span style="color:#f92672">-></span>setViewport({<span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">0</span>, (uint32_t) size.width, (uint32_t) size.height});
|
|
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> aspect <span style="color:#f92672">=</span> size.width <span style="color:#f92672">/</span> size.height;
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> left <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">2.0</span> <span style="color:#f92672">*</span> aspect;
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> right <span style="color:#f92672">=</span> <span style="color:#ae81ff">2.0</span> <span style="color:#f92672">*</span> aspect;
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> bottom <span style="color:#f92672">=</span> <span style="color:#f92672">-</span><span style="color:#ae81ff">2.0</span>;
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> top <span style="color:#f92672">=</span> <span style="color:#ae81ff">2.0</span>;
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> near <span style="color:#f92672">=</span> <span style="color:#ae81ff">0.0</span>;
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">double</span> far <span style="color:#f92672">=</span> <span style="color:#ae81ff">1.0</span>;
|
|
_camera<span style="color:#f92672">-></span>setProjection(Camera<span style="color:#f92672">::</span>Projection<span style="color:#f92672">::</span>ORTHO, left, right, bottom, top, near, far);
|
|
}
|
|
|
|
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">viewDidLoad</span> {
|
|
...
|
|
|
|
<span style="color:#75715e">// Give our View a starting size based on the drawable size.
|
|
</span><span style="color:#75715e"></span> [self resize:mtkView.drawableSize];
|
|
}
|
|
|
|
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">mtkView</span>(nonnull MTKView<span style="color:#f92672">*</span>)view <span style="color:#a6e22e">drawableSizeWillChange:</span>(CGSize)size {
|
|
[self resize:size];
|
|
}
|
|
</code></pre></div><p>Lastly, in order to render, we’ll call a few Filament API methods inside the <code>drawInMTKView:</code> method:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">drawInMTKView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view {
|
|
<span style="color:#66d9ef">if</span> (_renderer<span style="color:#f92672">-></span>beginFrame(_swapChain)) {
|
|
_renderer<span style="color:#f92672">-></span>render(_view);
|
|
_renderer<span style="color:#f92672">-></span>endFrame();
|
|
}
|
|
}
|
|
</code></pre></div><p>The <code>beginFrame</code> method instructs Filament to start rendering to our specific <code>SwapChain</code> instance. It returns <code>true</code> if the engine is ready for another frame. It returns <code>false</code> to signal us to skip this frame, which could happen if we’re sending frames down too quickly for the GPU to process.</p>
|
|
<p>At this point, you should be able to build and run the app, and you’ll see a blue screen.</p>
|
|
<p><img src="blue-screen.png" alt="blue screen after clearing"></p>
|
|
<h2 id="drawing-a-triangle">Drawing a Triangle</h2>
|
|
<p>In order to draw a triangle, we need to create vertex and index buffers to define its geometry. We’ll then create a <code>Renderable</code> component.</p>
|
|
<p>We’ll start by including some additional headers and adding a few new private vars:</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/VertexBuffer.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/IndexBuffer.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/RenderableManager.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e"></span>
|
|
...
|
|
|
|
VertexBuffer<span style="color:#f92672">*</span> _vertexBuffer;
|
|
IndexBuffer<span style="color:#f92672">*</span> _indexBuffer;
|
|
Entity _triangle;
|
|
</code></pre></div><p>First, we’ll define the data for a single vertex.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#66d9ef">struct</span> Vertex {
|
|
math<span style="color:#f92672">::</span>float2 position;
|
|
math<span style="color:#f92672">::</span>float3 color;
|
|
};
|
|
</code></pre></div><p>Creating a <code>VertexBuffer</code> and <code>IndexBuffer</code> is a matter of giving Filament a pointer to the data, along with information on its layout and size. Filament uses <code>BufferDescriptors</code> to accomplish this.</p>
|
|
<p>Inside <code>viewDidLoad</code>, we’ll statically define some verticies and indices and create a <code>BufferDescriptor</code> for each.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> Vertex TRIANGLE_VERTICES[<span style="color:#ae81ff">3</span>] <span style="color:#f92672">=</span> {
|
|
{ { <span style="color:#ae81ff">0.867</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">0.500</span>}, {<span style="color:#ae81ff">1.0</span>, <span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">0.0</span>} },
|
|
{ { <span style="color:#ae81ff">0.000</span>, <span style="color:#ae81ff">1.000</span>}, {<span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">1.0</span>, <span style="color:#ae81ff">0.0</span>} },
|
|
{ {<span style="color:#f92672">-</span><span style="color:#ae81ff">0.867</span>, <span style="color:#f92672">-</span><span style="color:#ae81ff">0.500</span>}, {<span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">1.0</span>} },
|
|
};
|
|
<span style="color:#66d9ef">static</span> <span style="color:#66d9ef">const</span> uint16_t TRIANGLE_INDICES[<span style="color:#ae81ff">3</span>] <span style="color:#f92672">=</span> { <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">1</span>, <span style="color:#ae81ff">2</span> };
|
|
|
|
VertexBuffer<span style="color:#f92672">::</span>BufferDescriptor vertices(TRIANGLE_VERTICES, <span style="color:#66d9ef">sizeof</span>(Vertex) <span style="color:#f92672">*</span> <span style="color:#ae81ff">3</span>, nullptr);
|
|
IndexBuffer<span style="color:#f92672">::</span>BufferDescriptor indices(TRIANGLE_INDICES, <span style="color:#66d9ef">sizeof</span>(uint16_t) <span style="color:#f92672">*</span> <span style="color:#ae81ff">3</span>, nullptr);
|
|
</code></pre></div><p>The last argument is an optional callback function, which will be called after Filament is done uploading the data to the GPU. Inside the callback, you’d typically release the memory of any buffers via a <code>free</code> or <code>delete</code> call. We pass <code>nullptr</code> because we don’t need a callback as our vertex and index buffer memory is static.</p>
|
|
<p>Now we can instantiate our <code>VertexBuffer</code> and <code>IndexBuffer</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">using Type <span style="color:#f92672">=</span> VertexBuffer<span style="color:#f92672">::</span>AttributeType;
|
|
|
|
<span style="color:#66d9ef">const</span> uint8_t stride <span style="color:#f92672">=</span> <span style="color:#66d9ef">sizeof</span>(Vertex);
|
|
_vertexBuffer <span style="color:#f92672">=</span> VertexBuffer<span style="color:#f92672">::</span>Builder()
|
|
.vertexCount(<span style="color:#ae81ff">3</span>)
|
|
.bufferCount(<span style="color:#ae81ff">1</span>)
|
|
.attribute(VertexAttribute<span style="color:#f92672">::</span>POSITION, <span style="color:#ae81ff">0</span>, Type<span style="color:#f92672">::</span>FLOAT2, offsetof(Vertex, position), stride)
|
|
.attribute(VertexAttribute<span style="color:#f92672">::</span>COLOR, <span style="color:#ae81ff">0</span>, Type<span style="color:#f92672">::</span>FLOAT3, offsetof(Vertex, color), stride)
|
|
.build(<span style="color:#f92672">*</span>_engine);
|
|
|
|
_indexBuffer <span style="color:#f92672">=</span> IndexBuffer<span style="color:#f92672">::</span>Builder()
|
|
.indexCount(<span style="color:#ae81ff">3</span>)
|
|
.bufferType(IndexBuffer<span style="color:#f92672">::</span>IndexType<span style="color:#f92672">::</span>USHORT)
|
|
.build(<span style="color:#f92672">*</span>_engine);
|
|
|
|
_vertexBuffer<span style="color:#f92672">-></span>setBufferAt(<span style="color:#f92672">*</span>_engine, <span style="color:#ae81ff">0</span>, std<span style="color:#f92672">::</span>move(vertices));
|
|
_indexBuffer<span style="color:#f92672">-></span>setBuffer(<span style="color:#f92672">*</span>_engine, std<span style="color:#f92672">::</span>move(indices));
|
|
</code></pre></div><p>We first create an <code>Entity</code> like we did for our camera. This time, we’re attaching a <code>Renderable</code> component to the entity. The <code>Renderable</code> component takes geometry defined by our vertex and index buffers, and makes the entity visible in our scene.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_triangle <span style="color:#f92672">=</span> utils<span style="color:#f92672">::</span>EntityManager<span style="color:#f92672">::</span>get().create();
|
|
|
|
using Primitive <span style="color:#f92672">=</span> RenderableManager<span style="color:#f92672">::</span>PrimitiveType;
|
|
RenderableManager<span style="color:#f92672">::</span>Builder(<span style="color:#ae81ff">1</span>)
|
|
.geometry(<span style="color:#ae81ff">0</span>, Primitive<span style="color:#f92672">::</span>TRIANGLES, _vertexBuffer, _indexBuffer, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">3</span>)
|
|
.culling(false)
|
|
.receiveShadows(false)
|
|
.castShadows(false)
|
|
.build(<span style="color:#f92672">*</span>_engine, _triangle);
|
|
|
|
<span style="color:#75715e">// Add the triangle to the scene.
|
|
</span><span style="color:#75715e"></span>_scene<span style="color:#f92672">-></span>addEntity(_triangle);
|
|
</code></pre></div><p>Destroy the entity and buffers in <code>dealloc</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-></span>destroy(_triangle);
|
|
EntityManager<span style="color:#f92672">::</span>get().destroy(_triangle);
|
|
_engine<span style="color:#f92672">-></span>destroy(_indexBuffer);
|
|
_engine<span style="color:#f92672">-></span>destroy(_vertexBuffer);
|
|
</code></pre></div><p>If you build and run the app now, you should see a plain white triangle. When we created the renderable, we didn’t specify any specific <code>Material</code> to use, so Filament used a default, white material. Let’s create a custom material to color the triangle.</p>
|
|
<p><img src="white-triangle.png" alt="a white triangle"></p>
|
|
<h2 id="compiling-a-custom-material">Compiling a Custom Material</h2>
|
|
<p>For simplicity, we’re going to compile a custom material at runtime. For production, we recommend using our matc tool to compile materials offline. You can download it as part of one of our <a href="https://github.com/google/filament/releases">releases</a>.</p>
|
|
<p>First, add a few more headers. We’ll be using Filament’s filamat library to compile a custom material.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c++" data-lang="c++"><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/Material.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/MaterialInstance.h></span><span style="color:#75715e">
|
|
</span><span style="color:#75715e"></span>
|
|
<span style="color:#75715e">#include</span> <span style="color:#75715e"><filamat/MaterialBuilder.h></span><span style="color:#75715e">
|
|
</span></code></pre></div><p>We’ll store our material in a new private var. We’ll also need one to store a material <em>instance</em>. You can think of a material as a “template”, where a material instance is an instantiation of the template (similar to OOP classes and instances). For more information on Filament materials, read the <a href="https://google.github.io/filament/Materials.html">Filament Materials Guide</a>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">Material<span style="color:#f92672">*</span> _material;
|
|
MaterialInstance<span style="color:#f92672">*</span> _materialInstance;
|
|
</code></pre></div><p>We’ll use the filamat library to compile a material into a package, which we can then load into Filament. The material will be simple; it will load the interpolated color attribute and set it as the <code>baseColor</code>.</p>
|
|
<p>Make sure to insert this code into <code>viewDidLoad</code> <em>before</em> we create our <code>Renderable</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// init must be called before we can build any materials.
|
|
</span><span style="color:#75715e"></span>filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>init();
|
|
|
|
<span style="color:#75715e">// Compile a custom material to use on the triangle.
|
|
</span><span style="color:#75715e"></span>filamat<span style="color:#f92672">::</span>Package pkg <span style="color:#f92672">=</span> filamat<span style="color:#f92672">::</span>MaterialBuilder()
|
|
<span style="color:#75715e">// The material name, only used for debugging purposes.
|
|
</span><span style="color:#75715e"></span> .name(<span style="color:#e6db74">"Triangle material"</span>)
|
|
<span style="color:#75715e">// Use the unlit shading mode, because we don't have any lights in our scene.
|
|
</span><span style="color:#75715e"></span> .shading(filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>Shading<span style="color:#f92672">::</span>UNLIT)
|
|
<span style="color:#75715e">// Expose the COLOR attribute visible to our shader code.
|
|
</span><span style="color:#75715e"></span> .require(VertexAttribute<span style="color:#f92672">::</span>COLOR)
|
|
<span style="color:#75715e">// Custom GLSL fragment shader
|
|
</span><span style="color:#75715e"></span> .material(<span style="color:#e6db74">"void material (inout MaterialInputs material) {"</span>
|
|
<span style="color:#e6db74">" prepareMaterial(material);"</span>
|
|
<span style="color:#e6db74">" material.baseColor = getColor();"</span>
|
|
<span style="color:#e6db74">"}"</span>)
|
|
<span style="color:#75715e">// Compile for Metal on mobile platforms.
|
|
</span><span style="color:#75715e"></span> .targetApi(filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>TargetApi<span style="color:#f92672">::</span>METAL)
|
|
.platform(filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>Platform<span style="color:#f92672">::</span>MOBILE)
|
|
.build();
|
|
assert(pkg.isValid());
|
|
|
|
<span style="color:#75715e">// shutdown should be called after all materials are built.
|
|
</span><span style="color:#75715e"></span>filamat<span style="color:#f92672">::</span>MaterialBuilder<span style="color:#f92672">::</span>shutdown();
|
|
</code></pre></div><p>Now that we have a <code>filamat::Package</code> representing the material, we can use it to instantiate a Filament <code>Material</code>. Note that again, we recommend using the matc command-line tool to compile material packages during your app’s compilation phase if possible, instead of at run-time.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// Create a Filament material from the Package.
|
|
</span><span style="color:#75715e"></span>_material <span style="color:#f92672">=</span> Material<span style="color:#f92672">::</span>Builder()
|
|
.package(pkg.getData(), pkg.getSize())
|
|
.build(<span style="color:#f92672">*</span>_engine);
|
|
_materialInstance <span style="color:#f92672">=</span> _material<span style="color:#f92672">-></span>getDefaultInstance();
|
|
</code></pre></div><p>Now we can use the <code>MaterialInstance</code> when creating our <code>Renderable</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// Create a renderable using our geometry and material.
|
|
</span><span style="color:#75715e"></span>using Primitive <span style="color:#f92672">=</span> RenderableManager<span style="color:#f92672">::</span>PrimitiveType;
|
|
RenderableManager<span style="color:#f92672">::</span>Builder(<span style="color:#ae81ff">1</span>)
|
|
.geometry(<span style="color:#ae81ff">0</span>, Primitive<span style="color:#f92672">::</span>TRIANGLES, _vertexBuffer, _indexBuffer, <span style="color:#ae81ff">0</span>, <span style="color:#ae81ff">3</span>)
|
|
<span style="color:#75715e">// Use the MaterialInstance we just created.
|
|
</span><span style="color:#75715e"></span> .material(<span style="color:#ae81ff">0</span>, _materialInstance)
|
|
.culling(false)
|
|
.receiveShadows(false)
|
|
.castShadows(false)
|
|
.build(<span style="color:#f92672">*</span>_engine, _triangle);
|
|
</code></pre></div><p>Lastly, we make sure to destroy everything inside <code>dealloc</code>.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">_engine<span style="color:#f92672">-></span>destroy(_materialInstance);
|
|
_engine<span style="color:#f92672">-></span>destroy(_material);
|
|
</code></pre></div><p>Build and run. You should see the same triangle, but with colors.</p>
|
|
<p><img src="colored-triangle.png" alt="the triangle with our custom material"></p>
|
|
<h2 id="animating-the-triangle">Animating the Triangle</h2>
|
|
<p>We’ll do this by animating a transform on our triangle entity. First, include a new header.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">#include</span> <span style="color:#75715e"><filament/TransformManager.h></span><span style="color:#75715e">
|
|
</span></code></pre></div><p>When we create our triangle entity, we’ll also attach a transform component. We’ve already seen two other components: <code>Renderable</code> and <code>Camera</code>. The <code>Transform</code> component allows us to set world-space transformations on entities.</p>
|
|
<p>Inside <code>viewDidLoad</code>, after we create the triangle entity’s <code>Renderable</code> component, we’ll also attach a <code>Transform</code> component.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c"><span style="color:#75715e">// Add a Transform component to the triangle, so we can animate it.
|
|
</span><span style="color:#75715e"></span>_engine<span style="color:#f92672">-></span>getTransformManager().create(_triangle);
|
|
</code></pre></div><p>Create a new function, <code>update</code>, and add call it inside the <code>drawInMTKView:</code> method.</p>
|
|
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-obj-c" data-lang="obj-c">- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">update</span> {
|
|
<span style="color:#66d9ef">auto</span><span style="color:#f92672">&</span> tm <span style="color:#f92672">=</span> _engine<span style="color:#f92672">-></span>getTransformManager();
|
|
<span style="color:#66d9ef">auto</span> i <span style="color:#f92672">=</span> tm.getInstance(_triangle);
|
|
<span style="color:#66d9ef">const</span> <span style="color:#66d9ef">auto</span> time <span style="color:#f92672">=</span> CACurrentMediaTime();
|
|
tm.setTransform(i, math<span style="color:#f92672">::</span>mat4f<span style="color:#f92672">::</span>rotation(time, math<span style="color:#f92672">::</span>float3 {<span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">0.0</span>, <span style="color:#ae81ff">1.0</span>}));
|
|
}
|
|
|
|
- (<span style="color:#66d9ef">void</span>)<span style="color:#a6e22e">drawInMTKView:</span>(nonnull MTKView<span style="color:#f92672">*</span>)view {
|
|
[self update];
|
|
<span style="color:#66d9ef">if</span> (_renderer<span style="color:#f92672">-></span>beginFrame(_swapChain)) {
|
|
_renderer<span style="color:#f92672">-></span>render(_view);
|
|
_renderer<span style="color:#f92672">-></span>endFrame();
|
|
}
|
|
}
|
|
</code></pre></div><p>Now we should see the triangle rotate around its z axis.</p>
|
|
<p><img src="rotating-triangle.gif" alt="a rotating triangle"></p>
|
|
<h2 id="next-steps">Next Steps</h2>
|
|
<p>In this guide we’ve covered how to install Filament with CocoaPods and get rendering using the Metal backend. We also compiled a custom material. Again, here’s the <a href="https://github.com/google/filament/tree/main/ios/samples/HelloCocoaPods">complete sample code</a> for the app. If you’re interesting in learning more, check out Filament’s additional <a href="https://github.com/google/filament/tree/main/ios/samples">iOS samples</a>. If you have any problems, feel free to open an <a href="https://github.com/google/filament/issues">issue</a>.</p>
|
|
|
|
</div>
|
|
|
|
|
|
</main>
|
|
|
|
</body>
|
|
</html>
|