mirror of
https://github.com/sotrh/learn-wgpu.git
synced 2024-11-08 07:10:33 +00:00
324 lines
70 KiB
HTML
324 lines
70 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en-US">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title>Uniform buffers and a 3d camera | Learn Wgpu</title>
|
|
<meta name="description" content="">
|
|
<meta name="generator" content="VuePress 1.4.0">
|
|
|
|
<meta property="article:modified_time" content="2021-02-19T04:30:18.000Z"><meta property="og:site_name" content="Learn Wgpu"><meta property="og:title" content="Uniform buffers and a 3d camera"><meta property="og:type" content="website"><meta property="og:url" content="/beginner/tutorial6-uniforms/"><meta name="twitter:title" content="Uniform buffers and a 3d camera"><meta name="twitter:url" content="/beginner/tutorial6-uniforms/"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:label1" content="Written by"><meta name="twitter:data2" content="Benjamin R Hansen"><meta name="twitter:creator" content="https://twitter.com/sotrh760">
|
|
<link rel="preload" href="/learn-wgpu/assets/css/0.styles.7acc6ef6.css" as="style"><link rel="preload" href="/learn-wgpu/assets/js/app.6593075e.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/2.da3fd46f.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/15.0fda50ec.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/22.2132e362.js" as="script"><link rel="prefetch" href="/learn-wgpu/assets/js/10.de8cadfb.js"><link rel="prefetch" href="/learn-wgpu/assets/js/11.65c8dcc2.js"><link rel="prefetch" href="/learn-wgpu/assets/js/12.26aabcb1.js"><link rel="prefetch" href="/learn-wgpu/assets/js/13.78eedf26.js"><link rel="prefetch" href="/learn-wgpu/assets/js/14.f852c057.js"><link rel="prefetch" href="/learn-wgpu/assets/js/16.f1c9d31a.js"><link rel="prefetch" href="/learn-wgpu/assets/js/17.8d0c6c00.js"><link rel="prefetch" href="/learn-wgpu/assets/js/18.d3691f26.js"><link rel="prefetch" href="/learn-wgpu/assets/js/19.e9350ae9.js"><link rel="prefetch" href="/learn-wgpu/assets/js/20.70e640cc.js"><link rel="prefetch" href="/learn-wgpu/assets/js/21.19d2890c.js"><link rel="prefetch" href="/learn-wgpu/assets/js/23.1a88f225.js"><link rel="prefetch" href="/learn-wgpu/assets/js/24.6da05868.js"><link rel="prefetch" href="/learn-wgpu/assets/js/25.2c8435d6.js"><link rel="prefetch" href="/learn-wgpu/assets/js/26.5b0dad74.js"><link rel="prefetch" href="/learn-wgpu/assets/js/27.04f788d9.js"><link rel="prefetch" href="/learn-wgpu/assets/js/28.a3ac06e2.js"><link rel="prefetch" href="/learn-wgpu/assets/js/29.281013fa.js"><link rel="prefetch" href="/learn-wgpu/assets/js/3.2a393c66.js"><link rel="prefetch" href="/learn-wgpu/assets/js/30.dc887be9.js"><link rel="prefetch" href="/learn-wgpu/assets/js/4.6b1a77da.js"><link rel="prefetch" href="/learn-wgpu/assets/js/5.6c35427e.js"><link rel="prefetch" href="/learn-wgpu/assets/js/6.7f771450.js"><link rel="prefetch" href="/learn-wgpu/assets/js/7.88810c81.js"><link rel="prefetch" href="/learn-wgpu/assets/js/8.d5139107.js"><link rel="prefetch" href="/learn-wgpu/assets/js/9.ecb6e79c.js">
|
|
<link rel="stylesheet" href="/learn-wgpu/assets/css/0.styles.7acc6ef6.css">
|
|
</head>
|
|
<body>
|
|
<div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="inner"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/learn-wgpu/" class="home-link router-link-active"><!----> <span class="site-name">Learn Wgpu</span></a> <div class="links"><!----> <div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div></div></div></header> <div class="sidebar-mask"></div> <div class="docs-layout"><aside class="sidebar"><!----> <ul class="sidebar-links"><li><a href="/learn-wgpu/" class="sidebar-link">Introduction</a></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading open"><span>Beginner</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/learn-wgpu/beginner/tutorial1-window/" class="sidebar-link">Dependencies and the window</a></li><li><a href="/learn-wgpu/beginner/tutorial2-swapchain/" class="sidebar-link">The Swapchain</a></li><li><a href="/learn-wgpu/beginner/tutorial3-pipeline/" class="sidebar-link">The Pipeline</a></li><li><a href="/learn-wgpu/beginner/tutorial4-buffer/" class="sidebar-link">Buffers and Indices</a></li><li><a href="/learn-wgpu/beginner/tutorial5-textures/" class="sidebar-link">Textures and bind groups</a></li><li><a href="/learn-wgpu/beginner/tutorial6-uniforms/" class="active sidebar-link">Uniform buffers and a 3d camera</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/learn-wgpu/beginner/tutorial6-uniforms/#a-perspective-camera" class="sidebar-link">A perspective camera</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/beginner/tutorial6-uniforms/#the-uniform-buffer" class="sidebar-link">The uniform buffer</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/beginner/tutorial6-uniforms/#uniform-buffers-and-bind-groups" class="sidebar-link">Uniform buffers and bind groups</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/beginner/tutorial6-uniforms/#using-the-uniforms-in-the-vertex-shader" class="sidebar-link">Using the uniforms in the vertex shader</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/beginner/tutorial6-uniforms/#a-controller-for-our-camera" class="sidebar-link">A controller for our camera</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/beginner/tutorial6-uniforms/#challenge" class="sidebar-link">Challenge</a></li></ul></li><li><a href="/learn-wgpu/beginner/tutorial7-instancing/" class="sidebar-link">Instancing</a></li><li><a href="/learn-wgpu/beginner/tutorial8-depth/" class="sidebar-link">The Depth Buffer</a></li><li><a href="/learn-wgpu/beginner/tutorial9-models/" class="sidebar-link">Model Loading</a></li></ul></section></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><span>Intermediate</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/learn-wgpu/intermediate/tutorial10-lighting/" class="sidebar-link">Working with Lights</a></li><li><a href="/learn-wgpu/intermediate/tutorial11-normals/" class="sidebar-link">Normal Mapping</a></li><li><a href="/learn-wgpu/intermediate/tutorial12-camera/" class="sidebar-link">A Better Camera</a></li><li><a href="/learn-wgpu/intermediate/tutorial13-threading/" class="sidebar-link">Multi-threading with Wgpu and Rayon</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>Showcase</span> <span class="arrow right"></span></p> <!----></section></li><li><a href="/learn-wgpu/news/" class="sidebar-link">News</a></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="uniform-buffers-and-a-3d-camera"><a href="#uniform-buffers-and-a-3d-camera" class="header-anchor">#</a> Uniform buffers and a 3d camera</h1> <p>While all of our previous work has seemed to be in 2d, we've actually been working in 3d the entire time! That's part of the reason why our <code>Vertex</code> structure has <code>position</code> be an array of 3 floats instead of just 2. We can't really see the 3d-ness of our scene, because we're viewing things head on. We're going to change our point of view by creating a <code>Camera</code>.</p> <h2 id="a-perspective-camera"><a href="#a-perspective-camera" class="header-anchor">#</a> A perspective camera</h2> <p>This tutorial is more about learning to use wgpu and less about linear algebra, so I'm going to gloss over a lot of the math involved. There's plenty of reading material online if you're interested in what's going on under the hood. The first thing to know is that we need <code>cgmath = "0.17"</code> in our <code>Cargo.toml</code>.</p> <p>Now that we have a math library, let's put it to use! Create a <code>Camera</code> struct above the <code>State</code> struct.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">struct</span> <span class="token type-definition class-name">Camera</span> <span class="token punctuation">{</span>
|
|
eye<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Point3</span><span class="token operator"><</span><span class="token keyword">f32</span><span class="token operator">></span><span class="token punctuation">,</span>
|
|
target<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Point3</span><span class="token operator"><</span><span class="token keyword">f32</span><span class="token operator">></span><span class="token punctuation">,</span>
|
|
up<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Vector3</span><span class="token operator"><</span><span class="token keyword">f32</span><span class="token operator">></span><span class="token punctuation">,</span>
|
|
aspect<span class="token punctuation">:</span> <span class="token keyword">f32</span><span class="token punctuation">,</span>
|
|
fovy<span class="token punctuation">:</span> <span class="token keyword">f32</span><span class="token punctuation">,</span>
|
|
znear<span class="token punctuation">:</span> <span class="token keyword">f32</span><span class="token punctuation">,</span>
|
|
zfar<span class="token punctuation">:</span> <span class="token keyword">f32</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">impl</span> <span class="token class-name">Camera</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">fn</span> <span class="token function-definition function">build_view_projection_matrix</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Matrix4</span><span class="token operator"><</span><span class="token keyword">f32</span><span class="token operator">></span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// 1.</span>
|
|
<span class="token keyword">let</span> view <span class="token operator">=</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Matrix4</span><span class="token punctuation">::</span><span class="token function">look_at</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">.</span>eye<span class="token punctuation">,</span> <span class="token keyword">self</span><span class="token punctuation">.</span>target<span class="token punctuation">,</span> <span class="token keyword">self</span><span class="token punctuation">.</span>up<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token comment">// 2.</span>
|
|
<span class="token keyword">let</span> proj <span class="token operator">=</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token function">perspective</span><span class="token punctuation">(</span><span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Deg</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">.</span>fovy<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">self</span><span class="token punctuation">.</span>aspect<span class="token punctuation">,</span> <span class="token keyword">self</span><span class="token punctuation">.</span>znear<span class="token punctuation">,</span> <span class="token keyword">self</span><span class="token punctuation">.</span>zfar<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token comment">// 3.</span>
|
|
<span class="token keyword">return</span> <span class="token constant">OPENGL_TO_WGPU_MATRIX</span> <span class="token operator">*</span> proj <span class="token operator">*</span> view<span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>The <code>build_view_projection_matrix</code> is where the magic happens.</p> <ol><li>The <code>view</code> matrix moves the world to be at the position and rotation of the camera. It's essentialy an inverse of whatever the transform matrix of the camera would be.</li> <li>The <code>proj</code> matrix wraps the scene to give the effect of depth. Without this, objects up close would be the same size as objects far away.</li> <li>The coordinate system in Wgpu is based on DirectX, and Metal's coordinate systems. That means that in <a href="https://github.com/gfx-rs/gfx/tree/master/src/backend/dx12#normalized-coordinates" target="_blank" rel="noopener noreferrer">normalized device coordinates<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></a> the x axis and y axis are in the range of -1.0 to +1.0, and the z axis is 0.0 to +1.0. The <code>cgmath</code> crate (as well as most game math crates) are built for OpenGL's coordinate system. This matrix will scale and translate our scene from OpenGL's coordinate sytem to WGPU's. We'll define it as follows.</li></ol> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token attribute attr-name">#[rustfmt::skip]</span>
|
|
<span class="token keyword">pub</span> <span class="token keyword">const</span> <span class="token constant">OPENGL_TO_WGPU_MATRIX</span><span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Matrix4</span><span class="token operator"><</span><span class="token keyword">f32</span><span class="token operator">></span> <span class="token operator">=</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Matrix4</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span>
|
|
<span class="token number">1.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span>
|
|
<span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">1.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span>
|
|
<span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.5</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span>
|
|
<span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.5</span><span class="token punctuation">,</span> <span class="token number">1.0</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
</code></pre></div><ul><li>Note: We don't explicitly <strong>need</strong> the <code>OPENGL_TO_WGPU_MATRIX</code>, but models centered on (0, 0, 0) will be halfway inside the clipping area. This is only an issue if you aren't using a camera matrix.</li></ul> <p>Now let's add a <code>camera</code> field to <code>State</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">struct</span> <span class="token type-definition class-name">State</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
camera<span class="token punctuation">:</span> <span class="token class-name">Camera</span><span class="token punctuation">,</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>window<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">Window</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// let diffuse_bind_group ...</span>
|
|
|
|
<span class="token keyword">let</span> camera <span class="token operator">=</span> <span class="token class-name">Camera</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// position the camera one unit up and 2 units back</span>
|
|
<span class="token comment">// +z is out of the screen</span>
|
|
eye<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">1.0</span><span class="token punctuation">,</span> <span class="token number">2.0</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token comment">// have it look at the origin</span>
|
|
target<span class="token punctuation">:</span> <span class="token punctuation">(</span><span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">,</span> <span class="token number">0.0</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token comment">// which way is "up"</span>
|
|
up<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Vector3</span><span class="token punctuation">::</span><span class="token function">unit_y</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
aspect<span class="token punctuation">:</span> sc_desc<span class="token punctuation">.</span>width <span class="token keyword">as</span> <span class="token keyword">f32</span> <span class="token operator">/</span> sc_desc<span class="token punctuation">.</span>height <span class="token keyword">as</span> <span class="token keyword">f32</span><span class="token punctuation">,</span>
|
|
fovy<span class="token punctuation">:</span> <span class="token number">45.0</span><span class="token punctuation">,</span>
|
|
znear<span class="token punctuation">:</span> <span class="token number">0.1</span><span class="token punctuation">,</span>
|
|
zfar<span class="token punctuation">:</span> <span class="token number">100.0</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
camera<span class="token punctuation">,</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>Now that we have our camera, and it can make us a view projection matrix, we need somewhere to put it. We also need some way of getting it into our shaders.</p> <h2 id="the-uniform-buffer"><a href="#the-uniform-buffer" class="header-anchor">#</a> The uniform buffer</h2> <p>Up to this point we've used <code>Buffer</code>s to store our vertex and index data, and even to load our textures. We are going to use them again to create what's known as a uniform buffer. A uniform is a blob of data that is available to every invocation of a set of shaders. We've technically already used uniforms for our texture and sampler. We're going to use them again to store our view projection matrix. To start let's create a struct to hold our <code>Uniforms</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token comment">// We need this for Rust to store our data correctly for the shaders</span>
|
|
<span class="token attribute attr-name">#[repr(C)]</span>
|
|
<span class="token comment">// This is so we can store this in a buffer</span>
|
|
<span class="token attribute attr-name">#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]</span>
|
|
<span class="token keyword">struct</span> <span class="token type-definition class-name">Uniforms</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// We can't use cgmath with bytemuck directly so we'll have</span>
|
|
<span class="token comment">// to convert the Matrix4 into a 4x4 f32 array</span>
|
|
view_proj<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token keyword">f32</span><span class="token punctuation">;</span> <span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token number">4</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">impl</span> <span class="token class-name">Uniforms</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">use</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">SquareMatrix</span><span class="token punctuation">;</span>
|
|
<span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
view_proj<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Matrix4</span><span class="token punctuation">::</span><span class="token function">identity</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">fn</span> <span class="token function-definition function">update_view_proj</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">,</span> camera<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">Camera</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>view_proj <span class="token operator">=</span> camera<span class="token punctuation">.</span><span class="token function">build_view_projection_matrix</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">into</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>Now that we have our data structured, let's make our <code>uniform_buffer</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token comment">// in new() after creating `camera`</span>
|
|
|
|
<span class="token keyword">let</span> <span class="token keyword">mut</span> uniforms <span class="token operator">=</span> <span class="token class-name">Uniforms</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
uniforms<span class="token punctuation">.</span><span class="token function">update_view_proj</span><span class="token punctuation">(</span><span class="token operator">&</span>camera<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token keyword">let</span> uniform_buffer <span class="token operator">=</span> device<span class="token punctuation">.</span><span class="token function">create_buffer_init</span><span class="token punctuation">(</span>
|
|
<span class="token operator">&</span><span class="token namespace">wgpu<span class="token punctuation">::</span>util<span class="token punctuation">::</span></span><span class="token class-name">BufferInitDescriptor</span> <span class="token punctuation">{</span>
|
|
label<span class="token punctuation">:</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token string">"Uniform Buffer"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
contents<span class="token punctuation">:</span> <span class="token namespace">bytemuck<span class="token punctuation">::</span></span><span class="token function">cast_slice</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token punctuation">[</span>uniforms<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
usage<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BufferUsage</span><span class="token punctuation">::</span><span class="token constant">UNIFORM</span> <span class="token operator">|</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BufferUsage</span><span class="token punctuation">::</span><span class="token constant">COPY_DST</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
</code></pre></div><h2 id="uniform-buffers-and-bind-groups"><a href="#uniform-buffers-and-bind-groups" class="header-anchor">#</a> Uniform buffers and bind groups</h2> <p>Cool, now that we have a uniform buffer, what do we do with it? The answer is we create a bind group for it. First we have to create the bind group layout.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">let</span> uniform_bind_group_layout <span class="token operator">=</span> device<span class="token punctuation">.</span><span class="token function">create_bind_group_layout</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroupLayoutDescriptor</span> <span class="token punctuation">{</span>
|
|
entries<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token punctuation">[</span>
|
|
<span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroupLayoutEntry</span> <span class="token punctuation">{</span>
|
|
binding<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
|
|
visibility<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">ShaderStage</span><span class="token punctuation">::</span><span class="token constant">VERTEX</span><span class="token punctuation">,</span>
|
|
ty<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindingType</span><span class="token punctuation">::</span><span class="token class-name">Buffer</span> <span class="token punctuation">{</span>
|
|
ty<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BufferBindingType</span><span class="token punctuation">::</span><span class="token class-name">Uniform</span><span class="token punctuation">,</span>
|
|
has_dynamic_offset<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
min_binding_size<span class="token punctuation">:</span> <span class="token class-name">None</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span><span class="token punctuation">,</span>
|
|
count<span class="token punctuation">:</span> <span class="token class-name">None</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
label<span class="token punctuation">:</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token string">"uniform_bind_group_layout"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
</code></pre></div><ol><li>We only really need camera information in the vertex shader, as that's what we'll use to manipulate our vertices.</li> <li>The <code>dynamic</code> field indicates whether this buffer will change size or not. This is useful if we want to store an array of things in our uniforms.</li></ol> <p>Now we can create the actual bind group.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">let</span> uniform_bind_group <span class="token operator">=</span> device<span class="token punctuation">.</span><span class="token function">create_bind_group</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroupDescriptor</span> <span class="token punctuation">{</span>
|
|
layout<span class="token punctuation">:</span> <span class="token operator">&</span>uniform_bind_group_layout<span class="token punctuation">,</span>
|
|
entries<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token punctuation">[</span>
|
|
<span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroupEntry</span> <span class="token punctuation">{</span>
|
|
binding<span class="token punctuation">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
|
|
resource<span class="token punctuation">:</span> uniform_buffer<span class="token punctuation">.</span><span class="token function">as_entire_binding</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
label<span class="token punctuation">:</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token string">"uniform_bind_group"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
</code></pre></div><p>Like with our texture, we need to register our <code>uniform_bind_group_layout</code> with the render pipeline.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">let</span> render_pipeline_layout <span class="token operator">=</span> device<span class="token punctuation">.</span><span class="token function">create_pipeline_layout</span><span class="token punctuation">(</span>
|
|
<span class="token operator">&</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">PipelineLayoutDescriptor</span> <span class="token punctuation">{</span>
|
|
label<span class="token punctuation">:</span> <span class="token class-name">Some</span><span class="token punctuation">(</span><span class="token string">"Render Pipeline Layout"</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
bind_group_layouts<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token punctuation">[</span>
|
|
<span class="token operator">&</span>texture_bind_group_layout<span class="token punctuation">,</span>
|
|
<span class="token operator">&</span>uniform_bind_group_layout<span class="token punctuation">,</span>
|
|
<span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
push_constant_ranges<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
</code></pre></div><p>Now we need to add <code>uniform_buffer</code> and <code>uniform_bind_group</code> to <code>State</code></p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">struct</span> <span class="token type-definition class-name">State</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
camera<span class="token punctuation">:</span> <span class="token class-name">Camera</span><span class="token punctuation">,</span>
|
|
uniforms<span class="token punctuation">:</span> <span class="token class-name">Uniforms</span><span class="token punctuation">,</span>
|
|
uniform_buffer<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Buffer</span><span class="token punctuation">,</span>
|
|
uniform_bind_group<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroup</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>window<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">Window</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
camera<span class="token punctuation">,</span>
|
|
uniforms<span class="token punctuation">,</span>
|
|
uniform_buffer<span class="token punctuation">,</span>
|
|
uniform_bind_group<span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>The final thing we need to do before we get into shaders is use the bind group in <code>render()</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code>render_pass<span class="token punctuation">.</span><span class="token function">set_pipeline</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">.</span>render_pipeline<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
render_pass<span class="token punctuation">.</span><span class="token function">set_bind_group</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">.</span>diffuse_bind_group<span class="token punctuation">,</span> <span class="token operator">&</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token comment">// NEW!</span>
|
|
render_pass<span class="token punctuation">.</span><span class="token function">set_bind_group</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">.</span>uniform_bind_group<span class="token punctuation">,</span> <span class="token operator">&</span><span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
render_pass<span class="token punctuation">.</span><span class="token function">set_vertex_buffer</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">.</span>vertex_buffer<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token punctuation">..</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
render_pass<span class="token punctuation">.</span><span class="token function">set_index_buffer</span><span class="token punctuation">(</span><span class="token keyword">self</span><span class="token punctuation">.</span>index_buffer<span class="token punctuation">.</span><span class="token function">slice</span><span class="token punctuation">(</span><span class="token punctuation">..</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">IndexFormat</span><span class="token punctuation">::</span><span class="token class-name">Uint16</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
|
|
render_pass<span class="token punctuation">.</span><span class="token function">draw_indexed</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">..</span><span class="token keyword">self</span><span class="token punctuation">.</span>num_indices<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">..</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
</code></pre></div><h2 id="using-the-uniforms-in-the-vertex-shader"><a href="#using-the-uniforms-in-the-vertex-shader" class="header-anchor">#</a> Using the uniforms in the vertex shader</h2> <p>Modify <code>shader.vert</code> to include the following.</p> <div class="language-glsl extra-class"><pre class="language-glsl"><code><span class="token macro property"><span class="token directive-hash">#</span><span class="token directive keyword">version</span> <span class="token expression"><span class="token number">450</span></span></span>
|
|
|
|
<span class="token keyword">layout</span><span class="token punctuation">(</span>location<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token keyword">vec3</span> a_position<span class="token punctuation">;</span>
|
|
<span class="token keyword">layout</span><span class="token punctuation">(</span>location<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">in</span> <span class="token keyword">vec2</span> a_tex_coords<span class="token punctuation">;</span>
|
|
|
|
<span class="token keyword">layout</span><span class="token punctuation">(</span>location<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">out</span> <span class="token keyword">vec2</span> v_tex_coords<span class="token punctuation">;</span>
|
|
|
|
<span class="token comment">// NEW!</span>
|
|
<span class="token keyword">layout</span><span class="token punctuation">(</span>set<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">,</span> binding<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">)</span> <span class="token comment">// 1.</span>
|
|
<span class="token keyword">uniform</span> Uniforms <span class="token punctuation">{</span>
|
|
<span class="token keyword">mat4</span> u_view_proj<span class="token punctuation">;</span> <span class="token comment">// 2.</span>
|
|
<span class="token punctuation">}</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
v_tex_coords <span class="token operator">=</span> a_tex_coords<span class="token punctuation">;</span> <span class="token comment">// UPDATED!</span>
|
|
gl_Position <span class="token operator">=</span> u_view_proj <span class="token operator">*</span> <span class="token keyword">vec4</span><span class="token punctuation">(</span>a_position<span class="token punctuation">,</span> <span class="token number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 3.</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><ol><li>Because we've created a new bind group, we need to specify which one we're using in the shader. The number is determined by our <code>render_pipeline_layout</code>. The <code>texture_bind_group_layout</code> is listed first, thus it's <code>set=0</code>, and <code>uniform_bind_group</code> is second, so it's <code>set=1</code>.</li> <li>The <code>uniform</code> block requires us to specify global identifiers for all the fields we intend to use. It's important to only specify fields that are actually in our uniform buffer, as trying to access data that isn't there may lead to undefined behavior.</li> <li>Multiplication order is important when it comes to matrices. The vector always goes on the right, and the matrices gone on the left in order of importance.</li></ol> <h2 id="a-controller-for-our-camera"><a href="#a-controller-for-our-camera" class="header-anchor">#</a> A controller for our camera</h2> <p>If you run the code right now, you should get something that looks like this.</p> <p><img src="/learn-wgpu/assets/img/static-tree.e1f31949.png" alt="./static-tree.png"></p> <p>The shape's less stretched now, but it's still pretty static. You can experiment with moving the camera position around, but most cameras in games move around. Since this tutorial is about using wgpu and not how to process user input, I'm just going to post the <code>CameraController</code> code below.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">struct</span> <span class="token type-definition class-name">CameraController</span> <span class="token punctuation">{</span>
|
|
speed<span class="token punctuation">:</span> <span class="token keyword">f32</span><span class="token punctuation">,</span>
|
|
is_up_pressed<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
|
|
is_down_pressed<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
|
|
is_forward_pressed<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
|
|
is_backward_pressed<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
|
|
is_left_pressed<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
|
|
is_right_pressed<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">impl</span> <span class="token class-name">CameraController</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>speed<span class="token punctuation">:</span> <span class="token keyword">f32</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
speed<span class="token punctuation">,</span>
|
|
is_up_pressed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
is_down_pressed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
is_forward_pressed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
is_backward_pressed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
is_left_pressed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
is_right_pressed<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">fn</span> <span class="token function-definition function">process_events</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">,</span> event<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">WindowEvent</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">bool</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">match</span> event <span class="token punctuation">{</span>
|
|
<span class="token class-name">WindowEvent</span><span class="token punctuation">::</span><span class="token class-name">KeyboardInput</span> <span class="token punctuation">{</span>
|
|
input<span class="token punctuation">:</span> <span class="token class-name">KeyboardInput</span> <span class="token punctuation">{</span>
|
|
state<span class="token punctuation">,</span>
|
|
virtual_keycode<span class="token punctuation">:</span> <span class="token class-name">Some</span><span class="token punctuation">(</span>keycode<span class="token punctuation">)</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">..</span>
|
|
<span class="token punctuation">}</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">..</span>
|
|
<span class="token punctuation">}</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">let</span> is_pressed <span class="token operator">=</span> <span class="token operator">*</span>state <span class="token operator">==</span> <span class="token class-name">ElementState</span><span class="token punctuation">::</span><span class="token class-name">Pressed</span><span class="token punctuation">;</span>
|
|
<span class="token keyword">match</span> keycode <span class="token punctuation">{</span>
|
|
<span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">Space</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>is_up_pressed <span class="token operator">=</span> is_pressed<span class="token punctuation">;</span>
|
|
<span class="token boolean">true</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">LShift</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>is_down_pressed <span class="token operator">=</span> is_pressed<span class="token punctuation">;</span>
|
|
<span class="token boolean">true</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">W</span> <span class="token operator">|</span> <span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">Up</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>is_forward_pressed <span class="token operator">=</span> is_pressed<span class="token punctuation">;</span>
|
|
<span class="token boolean">true</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">A</span> <span class="token operator">|</span> <span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">Left</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>is_left_pressed <span class="token operator">=</span> is_pressed<span class="token punctuation">;</span>
|
|
<span class="token boolean">true</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">S</span> <span class="token operator">|</span> <span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">Down</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>is_backward_pressed <span class="token operator">=</span> is_pressed<span class="token punctuation">;</span>
|
|
<span class="token boolean">true</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">D</span> <span class="token operator">|</span> <span class="token class-name">VirtualKeyCode</span><span class="token punctuation">::</span><span class="token class-name">Right</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>is_right_pressed <span class="token operator">=</span> is_pressed<span class="token punctuation">;</span>
|
|
<span class="token boolean">true</span>
|
|
<span class="token punctuation">}</span>
|
|
_ <span class="token operator">=></span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
_ <span class="token operator">=></span> <span class="token boolean">false</span><span class="token punctuation">,</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">fn</span> <span class="token function-definition function">update_camera</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">,</span> camera<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token keyword">mut</span> <span class="token class-name">Camera</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">use</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">InnerSpace</span><span class="token punctuation">;</span>
|
|
<span class="token keyword">let</span> forward <span class="token operator">=</span> camera<span class="token punctuation">.</span>target <span class="token operator">-</span> camera<span class="token punctuation">.</span>eye<span class="token punctuation">;</span>
|
|
<span class="token keyword">let</span> forward_norm <span class="token operator">=</span> forward<span class="token punctuation">.</span><span class="token function">normalize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token keyword">let</span> forward_mag <span class="token operator">=</span> forward<span class="token punctuation">.</span><span class="token function">magnitude</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token comment">// Prevents glitching when camera gets too close to the</span>
|
|
<span class="token comment">// center of the scene.</span>
|
|
<span class="token keyword">if</span> <span class="token keyword">self</span><span class="token punctuation">.</span>is_forward_pressed <span class="token operator">&&</span> forward_mag <span class="token operator">></span> <span class="token keyword">self</span><span class="token punctuation">.</span>speed <span class="token punctuation">{</span>
|
|
camera<span class="token punctuation">.</span>eye <span class="token operator">+=</span> forward_norm <span class="token operator">*</span> <span class="token keyword">self</span><span class="token punctuation">.</span>speed<span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token keyword">if</span> <span class="token keyword">self</span><span class="token punctuation">.</span>is_backward_pressed <span class="token punctuation">{</span>
|
|
camera<span class="token punctuation">.</span>eye <span class="token operator">-=</span> forward_norm <span class="token operator">*</span> <span class="token keyword">self</span><span class="token punctuation">.</span>speed<span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
|
|
<span class="token keyword">let</span> right <span class="token operator">=</span> forward_norm<span class="token punctuation">.</span><span class="token function">cross</span><span class="token punctuation">(</span>camera<span class="token punctuation">.</span>up<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token comment">// Redo radius calc in case the up/ down is pressed.</span>
|
|
<span class="token keyword">let</span> forward <span class="token operator">=</span> camera<span class="token punctuation">.</span>target <span class="token operator">-</span> camera<span class="token punctuation">.</span>eye<span class="token punctuation">;</span>
|
|
<span class="token keyword">let</span> forward_mag <span class="token operator">=</span> forward<span class="token punctuation">.</span><span class="token function">magnitude</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
|
|
<span class="token keyword">if</span> <span class="token keyword">self</span><span class="token punctuation">.</span>is_right_pressed <span class="token punctuation">{</span>
|
|
<span class="token comment">// Rescale the distance between the target and eye so </span>
|
|
<span class="token comment">// that it doesn't change. The eye therefore still </span>
|
|
<span class="token comment">// lies on the circle made by the target and eye.</span>
|
|
camera<span class="token punctuation">.</span>eye <span class="token operator">=</span> camera<span class="token punctuation">.</span>target <span class="token operator">-</span> <span class="token punctuation">(</span>forward <span class="token operator">+</span> right <span class="token operator">*</span> <span class="token keyword">self</span><span class="token punctuation">.</span>speed<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">normalize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> forward_mag<span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token keyword">if</span> <span class="token keyword">self</span><span class="token punctuation">.</span>is_left_pressed <span class="token punctuation">{</span>
|
|
camera<span class="token punctuation">.</span>eye <span class="token operator">=</span> camera<span class="token punctuation">.</span>target <span class="token operator">-</span> <span class="token punctuation">(</span>forward <span class="token operator">-</span> right <span class="token operator">*</span> <span class="token keyword">self</span><span class="token punctuation">.</span>speed<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">normalize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> forward_mag<span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>This code is not perfect. The camera slowly moves back when you rotate it. It works for our purposes though. Feel free to improve it!</p> <p>We still need to plug this into our existing code to make it do anything. Add the controller to <code>State</code> and create it in <code>new()</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">struct</span> <span class="token type-definition class-name">State</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
camera<span class="token punctuation">:</span> <span class="token class-name">Camera</span><span class="token punctuation">,</span>
|
|
<span class="token comment">// NEW!</span>
|
|
camera_controller<span class="token punctuation">:</span> <span class="token class-name">CameraController</span><span class="token punctuation">,</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token keyword">impl</span> <span class="token class-name">State</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>window<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">Window</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token keyword">let</span> camera_controller <span class="token operator">=</span> <span class="token class-name">CameraController</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token number">0.2</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token comment">// ...</span>
|
|
|
|
<span class="token keyword">Self</span> <span class="token punctuation">{</span>
|
|
<span class="token comment">// ...</span>
|
|
camera_controller<span class="token punctuation">,</span>
|
|
<span class="token comment">// ...</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>We're finally going to add some code to <code>input()</code> (assuming you haven't already)!</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">input</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">,</span> event<span class="token punctuation">:</span> <span class="token operator">&</span><span class="token class-name">WindowEvent</span><span class="token punctuation">)</span> <span class="token punctuation">-></span> <span class="token keyword">bool</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>camera_controller<span class="token punctuation">.</span><span class="token function">process_events</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>Up to this point, the camera controller isn't actually doing anything. The values in our uniform buffer need to be updated. There are a few main methods to do that.</p> <ol><li>We can create a separate buffer and copy it's contents to our <code>uniform_buffer</code>. The new buffer is known as a staging buffer. This method is usually how it's done as it allows the contents of the main buffer (in this case <code>uniform_buffer</code>) to only be accessible by the gpu. The gpu can do some speed optimizations which it couldn't if we could access the buffer via the cpu.</li> <li>We can call on of the mapping method's <code>map_read_async</code>, and <code>map_write_async</code> on the buffer itself. These allow us to access a buffer's contents directly, but requires us to deal with the <code>async</code> aspect of these methods this also requires our buffer to use the <code>BufferUsage::MAP_READ</code> and/or <code>BufferUsage::MAP_WRITE</code>. We won't talk about it here, but you check out <a href="../../showcase/windowless">Wgpu without a window</a> tutorial if you want to know more.</li> <li>We can use <code>write_buffer</code> on <code>queue</code>.</li></ol> <p>We're going to use option number 3.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">fn</span> <span class="token function-definition function">update</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>camera_controller<span class="token punctuation">.</span><span class="token function">update_camera</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">.</span>camera<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>uniforms<span class="token punctuation">.</span><span class="token function">update_view_proj</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">.</span>camera<span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token keyword">self</span><span class="token punctuation">.</span>queue<span class="token punctuation">.</span><span class="token function">write_buffer</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token keyword">self</span><span class="token punctuation">.</span>uniform_buffer<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token namespace">bytemuck<span class="token punctuation">::</span></span><span class="token function">cast_slice</span><span class="token punctuation">(</span><span class="token operator">&</span><span class="token punctuation">[</span><span class="token keyword">self</span><span class="token punctuation">.</span>uniforms<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
|
|
<span class="token punctuation">}</span>
|
|
</code></pre></div><p>That's all we need to do. If you run the code now you should see a pentagon with our tree texture that you can rotate around and zoom into with the wasd/arrow keys.</p> <h2 id="challenge"><a href="#challenge" class="header-anchor">#</a> Challenge</h2> <p>Have our model rotate on it's own independently of the the camera. <em>Hint: you'll need another matrix for this.</em></p> <div class="auto-github-link"><a href="https://github.com/sotrh/learn-wgpu/tree/master/code/beginner/tutorial6-uniforms/" target="_blank" rel="noopener noreferrer">Check out the code!</a> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></div></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">Last Updated: </span> <span class="time">2/19/2021, 4:30:18 AM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
|
|
←
|
|
<a href="/learn-wgpu/beginner/tutorial5-textures/" class="prev">
|
|
Textures and bind groups
|
|
</a></span> <span class="next"><a href="/learn-wgpu/beginner/tutorial7-instancing/">
|
|
Instancing
|
|
</a>
|
|
→
|
|
</span></p></div> </main></div></div><div class="global-ui"><!----></div></div>
|
|
<script src="/learn-wgpu/assets/js/app.6593075e.js" defer></script><script src="/learn-wgpu/assets/js/2.da3fd46f.js" defer></script><script src="/learn-wgpu/assets/js/15.0fda50ec.js" defer></script><script src="/learn-wgpu/assets/js/22.2132e362.js" defer></script>
|
|
</body>
|
|
</html>
|