You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
learn-wgpu/intermediate/tutorial11-normals/index.html

490 lines
133 KiB
HTML

<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Normal Mapping | Learn Wgpu</title>
<meta name="generator" content="VuePress 1.9.10">
<meta name="description" content="">
<link rel="preload" href="/learn-wgpu/assets/css/0.styles.81cb5453.css" as="style"><link rel="preload" href="/learn-wgpu/assets/js/app.8ee4646c.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/2.2ef7287a.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/1.eaeeb819.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/26.01a5696a.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/33.945dce62.js" as="script"><link rel="preload" href="/learn-wgpu/assets/js/37.5ecf7ce2.js" as="script"><link rel="prefetch" href="/learn-wgpu/assets/js/10.baf5d1fe.js"><link rel="prefetch" href="/learn-wgpu/assets/js/11.b8633c49.js"><link rel="prefetch" href="/learn-wgpu/assets/js/12.0b8835f1.js"><link rel="prefetch" href="/learn-wgpu/assets/js/13.af1bbe85.js"><link rel="prefetch" href="/learn-wgpu/assets/js/14.626c7cb3.js"><link rel="prefetch" href="/learn-wgpu/assets/js/15.5a8ea302.js"><link rel="prefetch" href="/learn-wgpu/assets/js/16.f4e8226b.js"><link rel="prefetch" href="/learn-wgpu/assets/js/17.ea2d3b52.js"><link rel="prefetch" href="/learn-wgpu/assets/js/18.4eb59bb5.js"><link rel="prefetch" href="/learn-wgpu/assets/js/19.48960e96.js"><link rel="prefetch" href="/learn-wgpu/assets/js/20.a01dc53f.js"><link rel="prefetch" href="/learn-wgpu/assets/js/21.5e81f33c.js"><link rel="prefetch" href="/learn-wgpu/assets/js/22.caf73498.js"><link rel="prefetch" href="/learn-wgpu/assets/js/23.ddff5db2.js"><link rel="prefetch" href="/learn-wgpu/assets/js/24.9ed2104b.js"><link rel="prefetch" href="/learn-wgpu/assets/js/25.1676676b.js"><link rel="prefetch" href="/learn-wgpu/assets/js/27.3b14c055.js"><link rel="prefetch" href="/learn-wgpu/assets/js/28.d1a4ff6c.js"><link rel="prefetch" href="/learn-wgpu/assets/js/29.ea2d6bbf.js"><link rel="prefetch" href="/learn-wgpu/assets/js/3.80d94795.js"><link rel="prefetch" href="/learn-wgpu/assets/js/30.f08bd92d.js"><link rel="prefetch" href="/learn-wgpu/assets/js/31.fbafea68.js"><link rel="prefetch" href="/learn-wgpu/assets/js/32.3d0e544d.js"><link rel="prefetch" href="/learn-wgpu/assets/js/34.0fe8e5e4.js"><link rel="prefetch" href="/learn-wgpu/assets/js/35.7c607f94.js"><link rel="prefetch" href="/learn-wgpu/assets/js/36.2e1d433e.js"><link rel="prefetch" href="/learn-wgpu/assets/js/38.b0253733.js"><link rel="prefetch" href="/learn-wgpu/assets/js/39.73123b03.js"><link rel="prefetch" href="/learn-wgpu/assets/js/4.ff267fdb.js"><link rel="prefetch" href="/learn-wgpu/assets/js/40.30864a0f.js"><link rel="prefetch" href="/learn-wgpu/assets/js/41.0b7d478f.js"><link rel="prefetch" href="/learn-wgpu/assets/js/42.4caac867.js"><link rel="prefetch" href="/learn-wgpu/assets/js/43.991fa7d2.js"><link rel="prefetch" href="/learn-wgpu/assets/js/44.27513d85.js"><link rel="prefetch" href="/learn-wgpu/assets/js/45.2ea5b108.js"><link rel="prefetch" href="/learn-wgpu/assets/js/46.61045c81.js"><link rel="prefetch" href="/learn-wgpu/assets/js/47.ea92b080.js"><link rel="prefetch" href="/learn-wgpu/assets/js/48.cc620f49.js"><link rel="prefetch" href="/learn-wgpu/assets/js/49.6426e080.js"><link rel="prefetch" href="/learn-wgpu/assets/js/5.250d79d3.js"><link rel="prefetch" href="/learn-wgpu/assets/js/50.05fd19ce.js"><link rel="prefetch" href="/learn-wgpu/assets/js/51.47410096.js"><link rel="prefetch" href="/learn-wgpu/assets/js/52.ecbe158e.js"><link rel="prefetch" href="/learn-wgpu/assets/js/53.8c976652.js"><link rel="prefetch" href="/learn-wgpu/assets/js/54.daee63ce.js"><link rel="prefetch" href="/learn-wgpu/assets/js/55.3799ddaa.js"><link rel="prefetch" href="/learn-wgpu/assets/js/56.74e5dcef.js"><link rel="prefetch" href="/learn-wgpu/assets/js/57.9cdfcb79.js"><link rel="prefetch" href="/learn-wgpu/assets/js/58.35b75a7b.js"><link rel="prefetch" href="/learn-wgpu/assets/js/59.5dd82339.js"><link rel="prefetch" href="/learn-wgpu/assets/js/60.1ced8124.js"><link rel="prefetch" href="/learn-wgpu/assets/js/61.f10516d3.js"><link rel="prefetch" href="/learn-wgpu/assets/js/62.fc721fd9.js"><link rel="prefetch" href="/learn-wgpu/assets/js/63.68526f40.js"><link rel="prefetch" href="/learn-wgpu/assets/js/64.b9a310af.js"><link rel="prefetch" href="/learn-wgpu/assets/js/65.e23a076d.js"><link rel="prefetch" href="/learn-wgpu/assets/js/66.19a033d1.js"><link rel="prefetch" href="/learn-wgpu/assets/js/67.ab338058.js"><link rel="prefetch" href="/learn-wgpu/assets/js/68.d785413d.js"><link rel="prefetch" href="/learn-wgpu/assets/js/8.2c0d831a.js"><link rel="prefetch" href="/learn-wgpu/assets/js/9.985bc61a.js"><link rel="prefetch" href="/learn-wgpu/assets/js/vendors~docsearch.efa6f8ef.js">
<link rel="stylesheet" href="/learn-wgpu/assets/css/0.styles.81cb5453.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/" aria-current="page" class="sidebar-link">Introduction</a></li><li><section class="sidebar-group depth-0"><p class="sidebar-heading"><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-surface/" class="sidebar-link">The Surface</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="sidebar-link">Uniform buffers and a 3d camera</a></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 open"><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/" aria-current="page" class="active sidebar-link">Normal Mapping</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/learn-wgpu/intermediate/tutorial11-normals/#tangent-space-to-world-space" class="sidebar-link">Tangent Space to World Space</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/intermediate/tutorial11-normals/#the-tangent-and-the-bitangent" class="sidebar-link">The tangent, and the bitangent</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/intermediate/tutorial11-normals/#world-space-to-tangent-space" class="sidebar-link">World Space to Tangent Space</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/intermediate/tutorial11-normals/#srgb-and-normal-textures" class="sidebar-link">Srgb and normal textures</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/intermediate/tutorial11-normals/#unrelated-stuff" class="sidebar-link">Unrelated stuff</a></li></ul></li><li><a href="/learn-wgpu/intermediate/tutorial12-camera/" class="sidebar-link">A Better Camera</a></li><li><a href="/learn-wgpu/intermediate/tutorial13-hdr/" class="sidebar-link">High Dynamic Range Rendering</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><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading"><span>News</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="normal-mapping"><a href="#normal-mapping" class="header-anchor">#</a> Normal Mapping</h1> <p>With just lighting, our scene is already looking pretty good. Still, our models are still overly smooth. This is understandable because we are using a very simple model. If we were using a texture that was supposed to be smooth, this wouldn't be a problem, but our brick texture is supposed to be rougher. We could solve this by adding more geometry, but that would slow our scene down, and it be would hard to know where to add new polygons. This is where normal mapping comes in.</p> <p>Remember in <a href="/learn-wgpu/beginner/tutorial7-instancing/#a-different-way-textures">the instancing tutorial</a>, we experimented with storing instance data in a texture? A normal map is doing just that with normal data! We'll use the normals in the normal map in our lighting calculation in addition to the vertex normal.</p> <p>The brick texture I found came with a normal map. Let's take a look at it!</p> <p><img src="/learn-wgpu/assets/img/cube-normal.025f2b53.png" alt="./cube-normal.png"></p> <p>The r, g, and b components of the texture correspond to the x, y, and z components or the normals. All the z values should be positive, that's why the normal map has a bluish tint.</p> <p>We'll need to modify our <code>Material</code> struct in <code>model.rs</code> to include a <code>normal_texture</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">struct</span> <span class="token type-definition class-name">Material</span> <span class="token punctuation">{</span>
<span class="token keyword">pub</span> name<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">,</span>
<span class="token keyword">pub</span> diffuse_texture<span class="token punctuation">:</span> <span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">,</span>
<span class="token keyword">pub</span> normal_texture<span class="token punctuation">:</span> <span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">,</span> <span class="token comment">// UPDATED!</span>
<span class="token keyword">pub</span> 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>
</code></pre></div><p>We'll have to update the <code>texture_bind_group_layout</code> to include the normal map as well.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">let</span> texture_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">&amp;</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">&amp;</span><span class="token punctuation">[</span>
<span class="token comment">// ...</span>
<span class="token comment">// normal map</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">2</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">ShaderStages</span><span class="token punctuation">::</span><span class="token constant">FRAGMENT</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">Texture</span> <span class="token punctuation">{</span>
multisampled<span class="token punctuation">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span>
sample_type<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureSampleType</span><span class="token punctuation">::</span><span class="token class-name">Float</span> <span class="token punctuation">{</span> filterable<span class="token punctuation">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
view_dimension<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureViewDimension</span><span class="token punctuation">::</span><span class="token constant">D2</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 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">3</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">ShaderStages</span><span class="token punctuation">::</span><span class="token constant">FRAGMENT</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">Sampler</span><span class="token punctuation">(</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">SamplerBindingType</span><span class="token punctuation">::</span><span class="token class-name">Filtering</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><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">&quot;texture_bind_group_layout&quot;</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>We'll need to actually load the normal map. We'll do this in the loop where we create the materials in the <code>load_model()</code> function in <code>resources.rs</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token comment">// resources.rs</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> materials <span class="token operator">=</span> <span class="token class-name">Vec</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>
<span class="token keyword">for</span> m <span class="token keyword">in</span> obj_materials<span class="token operator">?</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> diffuse_texture <span class="token operator">=</span> <span class="token function">load_texture</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>m<span class="token punctuation">.</span>diffuse_texture<span class="token punctuation">,</span> device<span class="token punctuation">,</span> queue<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span>
<span class="token comment">// NEW!</span>
<span class="token keyword">let</span> normal_texture <span class="token operator">=</span> <span class="token function">load_texture</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>m<span class="token punctuation">.</span>normal_texture<span class="token punctuation">,</span> device<span class="token punctuation">,</span> queue<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span>
materials<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token namespace">model<span class="token punctuation">::</span></span><span class="token class-name">Material</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span>
device<span class="token punctuation">,</span>
<span class="token operator">&amp;</span>m<span class="token punctuation">.</span>name<span class="token punctuation">,</span>
diffuse_texture<span class="token punctuation">,</span>
normal_texture<span class="token punctuation">,</span> <span class="token comment">// NEW!</span>
layout<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>You'll notice I'm using a <code>Material::new()</code> function we didn't have previously. Here's the code for that:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">impl</span> <span class="token class-name">Material</span> <span class="token punctuation">{</span>
<span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">new</span><span class="token punctuation">(</span>
device<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Device</span><span class="token punctuation">,</span>
name<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span>
diffuse_texture<span class="token punctuation">:</span> <span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">,</span>
normal_texture<span class="token punctuation">:</span> <span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">,</span> <span class="token comment">// NEW!</span>
layout<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroupLayout</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token keyword">Self</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> 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">&amp;</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>
entries<span class="token punctuation">:</span> <span class="token operator">&amp;</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> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindingResource</span><span class="token punctuation">::</span><span class="token class-name">TextureView</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>diffuse_texture<span class="token punctuation">.</span>view<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">BindGroupEntry</span> <span class="token punctuation">{</span>
binding<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
resource<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindingResource</span><span class="token punctuation">::</span><span class="token class-name">Sampler</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>diffuse_texture<span class="token punctuation">.</span>sampler<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>
<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">2</span><span class="token punctuation">,</span>
resource<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindingResource</span><span class="token punctuation">::</span><span class="token class-name">TextureView</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>normal_texture<span class="token punctuation">.</span>view<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">BindGroupEntry</span> <span class="token punctuation">{</span>
binding<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
resource<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindingResource</span><span class="token punctuation">::</span><span class="token class-name">Sampler</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>normal_texture<span class="token punctuation">.</span>sampler<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>name<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">Self</span> <span class="token punctuation">{</span>
name<span class="token punctuation">:</span> <span class="token class-name">String</span><span class="token punctuation">::</span><span class="token function">from</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">,</span>
diffuse_texture<span class="token punctuation">,</span>
normal_texture<span class="token punctuation">,</span> <span class="token comment">// NEW!</span>
bind_group<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 can use the texture in the fragment shader.</p> <div class="language-wgsl extra-class"><pre class="language-wgsl"><code><span class="token comment">// Fragment shader</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">group</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span> <span class="token punctuation">@</span><span class="token attributes attr-name">binding</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span>
<span class="token keyword">var</span> t_diffuse<span class="token punctuation">:</span> <span class="token builtin">texture_2d</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">;</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">group</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span><span class="token punctuation">@</span><span class="token attributes attr-name">binding</span><span class="token punctuation">(</span><span class="token int-literal number">1</span><span class="token punctuation">)</span>
<span class="token keyword">var</span> s_diffuse<span class="token punctuation">:</span> <span class="token builtin">sampler</span><span class="token punctuation">;</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">group</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span><span class="token punctuation">@</span><span class="token attributes attr-name">binding</span><span class="token punctuation">(</span><span class="token int-literal number">2</span><span class="token punctuation">)</span>
<span class="token keyword">var</span> t_normal<span class="token punctuation">:</span> <span class="token builtin">texture_2d</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">;</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">group</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span> <span class="token punctuation">@</span><span class="token attributes attr-name">binding</span><span class="token punctuation">(</span><span class="token int-literal number">3</span><span class="token punctuation">)</span>
<span class="token keyword">var</span> s_normal<span class="token punctuation">:</span> <span class="token builtin">sampler</span><span class="token punctuation">;</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">fragment</span>
<span class="token keyword">fn</span> <span class="token functions function">fs_main</span><span class="token punctuation">(</span>in<span class="token punctuation">:</span> <span class="token class-name">VertexOutput</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> object_color<span class="token punctuation">:</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span> <span class="token operator">=</span> <span class="token builtin">textureSample</span><span class="token punctuation">(</span>t_diffuse<span class="token punctuation">,</span> s_diffuse<span class="token punctuation">,</span> in<span class="token punctuation">.</span>tex_coords<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> object_normal<span class="token punctuation">:</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span> <span class="token operator">=</span> <span class="token builtin">textureSample</span><span class="token punctuation">(</span>t_normal<span class="token punctuation">,</span> s_normal<span class="token punctuation">,</span> in<span class="token punctuation">.</span>tex_coords<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// We don't need (or want) much ambient light, so 0.1 is fine</span>
<span class="token keyword">let</span> ambient_strength <span class="token operator">=</span> <span class="token decimal-float-literal number">0.1</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> ambient_color <span class="token operator">=</span> light<span class="token punctuation">.</span>color <span class="token operator">*</span> ambient_strength<span class="token punctuation">;</span>
<span class="token comment">// Create the lighting vectors</span>
<span class="token keyword">let</span> tangent_normal <span class="token operator">=</span> object_normal<span class="token punctuation">.</span>xyz <span class="token operator">*</span> <span class="token decimal-float-literal number">2.0</span> <span class="token operator">-</span> <span class="token decimal-float-literal number">1.0</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> light_dir <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>light<span class="token punctuation">.</span>position <span class="token operator">-</span> in<span class="token punctuation">.</span>world_position<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> view_dir <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>camera<span class="token punctuation">.</span>view_pos<span class="token punctuation">.</span>xyz <span class="token operator">-</span> in<span class="token punctuation">.</span>world_position<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> half_dir <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>view_dir <span class="token operator">+</span> light_dir<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> diffuse_strength <span class="token operator">=</span> <span class="token builtin">max</span><span class="token punctuation">(</span><span class="token builtin">dot</span><span class="token punctuation">(</span>tangent_normal<span class="token punctuation">,</span> light_dir<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token decimal-float-literal number">0.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> diffuse_color <span class="token operator">=</span> light<span class="token punctuation">.</span>color <span class="token operator">*</span> diffuse_strength<span class="token punctuation">;</span>
<span class="token keyword">let</span> specular_strength <span class="token operator">=</span> <span class="token builtin">pow</span><span class="token punctuation">(</span><span class="token builtin">max</span><span class="token punctuation">(</span><span class="token builtin">dot</span><span class="token punctuation">(</span>tangent_normal<span class="token punctuation">,</span> half_dir<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token decimal-float-literal number">0.0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token decimal-float-literal number">32.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> specular_color <span class="token operator">=</span> specular_strength <span class="token operator">*</span> light<span class="token punctuation">.</span>color<span class="token punctuation">;</span>
<span class="token keyword">let</span> result <span class="token operator">=</span> <span class="token punctuation">(</span>ambient_color <span class="token operator">+</span> diffuse_color <span class="token operator">+</span> specular_color<span class="token punctuation">)</span> <span class="token operator">*</span> object_color<span class="token punctuation">.</span>xyz<span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">(</span>result<span class="token punctuation">,</span> object_color<span class="token punctuation">.</span>a<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>If we run the code now, you'll notice things don't look quite right. Let's compare our results with the last tutorial.</p> <p><img src="/learn-wgpu/assets/img/normal_mapping_wrong.4b8065ba.png" alt=""> <img src="/learn-wgpu/assets/img/ambient_diffuse_specular_lighting.76386148.png" alt=""></p> <p>Parts of the scene are dark when they should be lit up, and vice versa.</p> <h2 id="tangent-space-to-world-space"><a href="#tangent-space-to-world-space" class="header-anchor">#</a> Tangent Space to World Space</h2> <p>I mentioned briefly in the <a href="/learn-wgpu/intermediate/tutorial10-lighting/#the-normal-matrix">lighting tutorial</a>, that we were doing our lighting calculation in &quot;world space&quot;. This meant that the entire scene was oriented with respect to the <em>world's</em> coordinate system. When we pull the normal data from our normal texture, all the normals are in what's known as pointing roughly in the positive z direction. That means that our lighting calculation thinks all of the surfaces of our models are facing in roughly the same direction. This is referred to as <code>tangent space</code>.</p> <p>If we remember the <a href="/learn-wgpu/intermediate/tutorial10-lighting/#">lighting-tutorial</a>, we used the vertex normal to indicate the direction of the surface. It turns out we can use that to transform our normals from <code>tangent space</code> into <code>world space</code>. In order to do that we need to draw from the depths of linear algebra.</p> <p>We can create a matrix that represents a coordinate system using 3 vectors that are perpendicular (or orthonormal) to each other. Basically, we define the x, y, and z axes of our coordinate system.</p> <div class="language-wgsl extra-class"><pre class="language-wgsl"><code><span class="token keyword">let</span> coordinate_system <span class="token operator">=</span> <span class="token builtin">mat3x3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">(</span>
<span class="token builtin">vec3</span><span class="token punctuation">(</span><span class="token int-literal number">1</span><span class="token punctuation">,</span> <span class="token int-literal number">0</span><span class="token punctuation">,</span> <span class="token int-literal number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// x axis (right)</span>
<span class="token builtin">vec3</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">,</span> <span class="token int-literal number">1</span><span class="token punctuation">,</span> <span class="token int-literal number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// y axis (up)</span>
<span class="token builtin">vec3</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">,</span> <span class="token int-literal number">0</span><span class="token punctuation">,</span> <span class="token int-literal number">1</span><span class="token punctuation">)</span> <span class="token comment">// z axis (forward)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>We're going to create a matrix that will represent the coordinate space relative to our vertex normals. We're then going to use that to transform our normal map data to be in world space.</p> <h2 id="the-tangent-and-the-bitangent"><a href="#the-tangent-and-the-bitangent" class="header-anchor">#</a> The tangent, and the bitangent</h2> <p>We have one of the 3 vectors we need, the normal. What about the others? These are the tangent and bitangent vectors. A tangent represents any vector that is parallel with a surface (aka. doesn't intersect with it). The tangent is always perpendicular to the normal vector. The bitangent is a tangent vector that is perpendicular to the other tangent vector. Together the tangent, bitangent, and normal represent the x, y, and z axes respectively.</p> <p>Some model formats include the tanget and bitangent (sometimes called the binormal) in the vertex data, but OBJ does not. We'll have to calculate them manually. Luckily we can derive our tangent and bitangent from our existing vertex data. Take a look at the following diagram.</p> <p><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAZAAAAGQCAYAAACAvzbMAAABg2lDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TS0UqDmaQ4pChOlkQFXHUKhShQqgVWnUwufQLmrQkKS6OgmvBwY/FqoOLs64OroIg+AHi5Oik6CIl/i8ptIjx4Lgf7+497t4BQrPCdKtnHNAN20wnE1I2tyqFXxFCECKigMKs2pwsp+A7vu4R4OtdnGf5n/tz9Gt5iwEBiXiW1UybeIN4etOucd4nFllJ0YjPicdMuiDxI9dVj984F10WeKZoZtLzxCKxVOxitYtZydSJp4hjmm5QvpD1WOO8xVmv1Fn7nvyFkbyxssx1msNIYhFLkCFBRR1lVGAjTqtBioU07Sd8/FHXL5NLJVcZjBwLqEKH4vrB/+B3t1ZhcsJLiiSA0IvjfIwA4V2g1XCc72PHaZ0AwWfgyuj4q01g5pP0RkeLHQED28DFdUdT94DLHWDoqaaYiisFaQqFAvB+Rt+UAwZvgb41r7f2Pk4fgAx1lboBDg6B0SJlr/u8u7e7t3/PtPv7AfEscnMV1+LuAAAABmJLR0QAAACSAP8q1jhsAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5AUDFCAS5kaN7AAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAApfSURBVHja7d1PbhNnHMfh14YjINTsuEXUayCx8BFKRGRlWXVZdYkiV0mP4EUkroFyi+yool4huCsH1409M55/7++d51khSAMYaT79vjOJZ2m12SQAaGjuJQBAQAAQEAAEBAABAQABAUBAABAQAAQEAAEBAAEBQEAAEBAABAQAAQEAAQFAQAAQEAAEBAABAQABAUBAABAQAAQEAAEBAAEBQEAAEBAABAQAAQEAAQFAQAAQEAAEBAABAQABAUBAAMjXay9BbN8uZ88/PlttvCDAYGZptXHVCR6OfUICCAiN4yEkwFDcAxEaAAERBQABQXAAAUFEAAEhbAhEBBAQ8RARQEAY/sIvIoCAiIeIAALC8Bd6EQEERDxEBBAQhr+wiwggIOIhIoCAACAgBFkCVgggIAAICAACQg1jvgGUN58CBAQAAbFCrA8gX94TPWN9PxklHICAiItQAINzhAWAgAAgIAAICAACAgACAoCAACAgAAgIAAICAAICgIAAICAACAgAAgIAAgKAgAAgIAAICAACAgACAoCAACAgAAgIAAICAAICgIAAkJXXXgKA8S0eb59/vH77UUAAqB+O/Z/LPSSOsAAyisf+r1d9jIAAEC4kAgKQ4fqIEBIBAZhAfAQEgGzWiIAACImAAAiJgAAUra+v8RgyJAICUOgiERCAAi/uQ1zg+/49fCsTgID/99/kz9LXcZmAAASNw9gEBKDwQPS1QgQEEAcEBBAIBARAHDriJjogDlgggEAQe30ICCAOBVvOn9LaAgEEgqbx6JuAgDgw8kX++vurXj7v+ZtPAgIIhBWQ3+8nIJTv179//PiPn8SBkBfrpn+evteHgDCdcOz/XICQCMR0ArGcP7U6xhojHgLCtOKx/+sjRkQcrIc+/i5DhUNAoMc1IhACUXo8BIRpro+Wa0QcxKGt8zef0v0/f/7v71X3GGvscAgIHIiIQIhD3/bjEWl1CAgciMhi+cXrIBCjvj7HVkhO8UgppVlabTb+2eL5djmr/bFnq+n8E2/Xw/r6fbvPIyTiMLLdkOQWDgERkLBxqNI2HiIiELnKKR4pOcIiWByGtL5+P5mIiINwCAiTD0RX62P/80UPiUCIh4BgPVgj4iAcAoJA1FkROV20x4qIQIhHrtxED6qzm+g7X3TX9uLY9sJ/7Pipzufr+viqzZ9FHCg9HhbIhBfESxfbU8/7u/hcVRf/qDe0BYISwyEgE9blxbrUC3+dv5M4MOV4pJTS3D8duQet6cdZFkQKR9R4CIiLdauPi3jhB6ujO46woKH7qzsvApMOh4CQjcXyS62Vsr2Xcuj4aO2lRDwG5Qhrghfrrj6uy89Vx3L+dDAe558/WB+IhwVCHWerTa2vBbn67SarP/ehACyv7o5GYOwLt3AgHAJCqj4yarIYqj5Xkwvv9mN3QyIciEe+fCV65BA83qbPv1+0Wh9Nvnq86tHVMS/8XR9hCQfCISBFx2PXbkhOObaK/nUNXQVEOBCP+hxhFaIqGr7wTTgQDgHh2cPFj9Xx7uZm0vG4r7gJLxyIh4CQUvr5wyw9HIjJsZAgHAhHl9wDCRiPKvsRmdLxVdUKEQ7EwwKBo4HYD4lwIB4WiPVR0+4KcQMdhKMPvpUJgHicxBEWgHBYIKX7elfvtNHxFYiHBQIgHBYIw6wQXwcC4mGBUBmJul+JDoiHgGBtgHBkwxFWMPvfhRcQDwsEQDgsEPLgEV4QDwsEQDgsEADxsEAYmcd4QTwEhJPDsf9zQgLCMRRHWIEsHm9fjEdVXEA8sEAAhCMb3lAqkCZvKHX918oLhnhggQAIR37cAwHEAwEpXd0nrDyJxVTCIR4CAmB1CAh92X4X3qp1YX0gHgzFTfSAvKEUwoGA0ElI9vkuvIgHAgIIB9lyDwQQDywQQDiwQADxQEBoa/sIL4gHOXGEBQgHFgge4UU8sEAA4UBA2Gryfh5f77xNC+KBgAhHg3C89N881PxvfDsThIMheUfCDOPRlncjRDwQEAFpHZHlL5fCgnggIOJhoSAc5MNjvBO0u0pAPDiVm+iAcGCB5CbnR3GtEMQDCwQQDiwQKwTEAwuEzHkSC+HAArFCQDwQEKwPxIM4HGENuEJy/cJCEA4skAARsT4QDywQwhEPhAMLxAoRD8QDCwQQDiwQMl0h1gfigYAgHogH2XCENeIK8VgvwoEFwskRsT4QDywQsiIeCAcWiBUiHogHFgggHFggZLpCrA/EAwEREfFAPMiaIywQDrBAprxCrA/EAwsE8UA4sEDof4WIB+KBBUJKKaXF42168DIgHFggnOLdzU2nH4d4gICIyH9+fTl/8kKJB4zGEVaAiDxcXFgdCAcCQvdrBPEAAQGEgzDcAwHxAAskusXjrRcB4cACAcQDC4QMeYRXOMACAcQDAQHEg+lwhAXCARZIZJ7AEg+wQADhwAIhT57AEg+wQEA4wAIBxAMBAcQDKjnCAuEACyQqj/CKB1ggIBxggZAnj/CKB1ggIBxggYB4gIAA4gG1OMIC4QALJCKP8IoHWCAgHGCBkCeP8IoHWCAgHGCBgHiABQLCAVggIB4gIMXyCK94QGSOsBAOwAIpmUd4xQMsEBAOsEBAPMACAeEALBAQDxCQYnmEVzwgOkdYCAdggZTKI7ziARYICAdYICAeYIGAcAAWCOIBCEixPMIrHlACR1gIB2CBlMgjvOIBFggIB1ggIB6ABYJwABYI4gFYIMXyCK9wgAUC4gECQp5Kf4RXPCA2R1gIB2CBIB6ABYJwABYI4iEeYIHQytQe4RUOsEBAPAABiSLyI7ziAdPgCAvhACwQxAOwQBAOwAJBPAALhJOV9AivcAAWCOIBCEhJcn2EVzyAXY6wWjh0LLV++9HqAASE+uGo++viAQiIeBRLOAABCej6+6vnH49xP0Q8gDpmabXZeBnirI8+gyIcQBOewgq4TnYXingAY3GEFTgkXawR4QAskAF4PBdAQIpZIeIBCIgVMtjqEA9AQLA6gNF4jPdEkR7pFQ7AAslIlKMs8QAsEEuk0foQDsACQTwAASnR0EdZ4gHkwhFWR/o+yhIOwAJBPAALhP5XyLF4CAcgICJidQDh+G68GbI6gAjcA+lY26eyxAOIwhFWT5oeZTmyAiwQOlkiu+EQD0BAaLQ+hAMQECvE6gCK4x7IAI7dD9ldH6IBCAiVISnt/dUBAQGAWtwDAUBAABAQAAQEAAEBAAEBQEAAEBAABAQAAQEAAQFAQAAQEAAEBAABAQABAUBAABAQAAQEAAEBAAEBQEAAEBAABAQAAQEAAQFAQAAQEAAEBAABAQABAUBAABAQAAQEAAEBAAEBQEAAEBAABAQABAQAAQFAQAAQEAAEBAAEBAABAUBAABAQAAQEAAQEAAEBQEAAEBAABAQABAQAAQFAQAAQEAAEBAAEBAABAUBAABAQAAQEAAQEAAEBQEAAEBAABAQABAQAAQFAQAAQEAAExEsAgIAAICAACAgAAgIAAgKAgAAgIAAICAACAgACAoCAACAgAAgIAAICAAICgIAAICAACAgAU/EvfiluCemVcXgAAAAASUVORK5CYII=" alt=""></p> <p>Basically, we can use the edges of our triangles, and our normal to calculate the tangent and bitangent. But first, we need to update our <code>ModelVertex</code> struct in <code>model.rs</code>.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token attribute attr-name">#[repr(C)]</span>
<span class="token attribute attr-name">#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]</span>
<span class="token keyword">pub</span> <span class="token keyword">struct</span> <span class="token type-definition class-name">ModelVertex</span> <span class="token punctuation">{</span>
<span class="token keyword">pub</span> position<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">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token keyword">pub</span> tex_coords<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">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token keyword">pub</span> normal<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">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token comment">// NEW!</span>
<span class="token keyword">pub</span> tangent<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">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token keyword">pub</span> bitangent<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">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span>
</code></pre></div><p>We'll need to upgrade our <code>VertexBufferLayout</code> as well.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">impl</span> <span class="token class-name">Vertex</span> <span class="token keyword">for</span> <span class="token class-name">ModelVertex</span> <span class="token punctuation">{</span>
<span class="token keyword">fn</span> <span class="token function-definition function">desc</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">VertexBufferLayout</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'static</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">use</span> <span class="token namespace">std<span class="token punctuation">::</span></span>mem<span class="token punctuation">;</span>
<span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">VertexBufferLayout</span> <span class="token punctuation">{</span>
array_stride<span class="token punctuation">:</span> <span class="token namespace">mem<span class="token punctuation">::</span></span><span class="token function">size_of</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token class-name">ModelVertex</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BufferAddress</span><span class="token punctuation">,</span>
step_mode<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">VertexStepMode</span><span class="token punctuation">::</span><span class="token class-name">Vertex</span><span class="token punctuation">,</span>
attributes<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span>
<span class="token comment">// ...</span>
<span class="token comment">// Tangent and bitangent</span>
<span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">VertexAttribute</span> <span class="token punctuation">{</span>
offset<span class="token punctuation">:</span> <span class="token namespace">mem<span class="token punctuation">::</span></span><span class="token function">size_of</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token punctuation">[</span><span class="token keyword">f32</span><span class="token punctuation">;</span> <span class="token number">8</span><span class="token punctuation">]</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BufferAddress</span><span class="token punctuation">,</span>
shader_location<span class="token punctuation">:</span> <span class="token number">3</span><span class="token punctuation">,</span>
format<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">VertexFormat</span><span class="token punctuation">::</span><span class="token class-name">Float32x3</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">VertexAttribute</span> <span class="token punctuation">{</span>
offset<span class="token punctuation">:</span> <span class="token namespace">mem<span class="token punctuation">::</span></span><span class="token function">size_of</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token punctuation">[</span><span class="token keyword">f32</span><span class="token punctuation">;</span> <span class="token number">11</span><span class="token punctuation">]</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BufferAddress</span><span class="token punctuation">,</span>
shader_location<span class="token punctuation">:</span> <span class="token number">4</span><span class="token punctuation">,</span>
format<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">VertexFormat</span><span class="token punctuation">::</span><span class="token class-name">Float32x3</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 punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Now we can calculate the new tangent and bitangent vectors. Update the mesh generation in <code>load_model()</code> in <code>resource.rs</code> to use the following code:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">let</span> meshes <span class="token operator">=</span> models
<span class="token punctuation">.</span><span class="token function">into_iter</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>m<span class="token closure-punctuation punctuation">|</span></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> vertices <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">..</span>m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>positions<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">3</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token closure-params"><span class="token closure-punctuation punctuation">|</span>i<span class="token closure-punctuation punctuation">|</span></span> <span class="token namespace">model<span class="token punctuation">::</span></span><span class="token class-name">ModelVertex</span> <span class="token punctuation">{</span>
position<span class="token punctuation">:</span> <span class="token punctuation">[</span>
m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>positions<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>positions<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">3</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>positions<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">3</span> <span class="token operator">+</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
tex_coords<span class="token punctuation">:</span> <span class="token punctuation">[</span>m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>texcoords<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">]</span><span class="token punctuation">,</span> m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>texcoords<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">2</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
normal<span class="token punctuation">:</span> <span class="token punctuation">[</span>
m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>normals<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>normals<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">3</span> <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>normals<span class="token punctuation">[</span>i <span class="token operator">*</span> <span class="token number">3</span> <span class="token operator">+</span> <span class="token number">2</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">// We'll calculate these later</span>
tangent<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">3</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
bitangent<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">3</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 function">collect</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token class-name">Vec</span><span class="token operator">&lt;</span>_<span class="token operator">&gt;&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> indices <span class="token operator">=</span> <span class="token operator">&amp;</span>m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>indices<span class="token punctuation">;</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> triangles_included <span class="token operator">=</span> <span class="token macro property">vec!</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">;</span> vertices<span class="token punctuation">.</span><span class="token function">len</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">// Calculate tangents and bitangets. We're going to</span>
<span class="token comment">// use the triangles, so we need to loop through the</span>
<span class="token comment">// indices in chunks of 3</span>
<span class="token keyword">for</span> c <span class="token keyword">in</span> indices<span class="token punctuation">.</span><span class="token function">chunks</span><span class="token punctuation">(</span><span class="token number">3</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> v0 <span class="token operator">=</span> vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> v1 <span class="token operator">=</span> vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> v2 <span class="token operator">=</span> vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> pos0<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">&lt;</span>_<span class="token operator">&gt;</span> <span class="token operator">=</span> v0<span class="token punctuation">.</span>position<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 keyword">let</span> pos1<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">&lt;</span>_<span class="token operator">&gt;</span> <span class="token operator">=</span> v1<span class="token punctuation">.</span>position<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 keyword">let</span> pos2<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">&lt;</span>_<span class="token operator">&gt;</span> <span class="token operator">=</span> v2<span class="token punctuation">.</span>position<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 keyword">let</span> uv0<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Vector2</span><span class="token operator">&lt;</span>_<span class="token operator">&gt;</span> <span class="token operator">=</span> v0<span class="token punctuation">.</span>tex_coords<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 keyword">let</span> uv1<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Vector2</span><span class="token operator">&lt;</span>_<span class="token operator">&gt;</span> <span class="token operator">=</span> v1<span class="token punctuation">.</span>tex_coords<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 keyword">let</span> uv2<span class="token punctuation">:</span> <span class="token namespace">cgmath<span class="token punctuation">::</span></span><span class="token class-name">Vector2</span><span class="token operator">&lt;</span>_<span class="token operator">&gt;</span> <span class="token operator">=</span> v2<span class="token punctuation">.</span>tex_coords<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">// Calculate the edges of the triangle</span>
<span class="token keyword">let</span> delta_pos1 <span class="token operator">=</span> pos1 <span class="token operator">-</span> pos0<span class="token punctuation">;</span>
<span class="token keyword">let</span> delta_pos2 <span class="token operator">=</span> pos2 <span class="token operator">-</span> pos0<span class="token punctuation">;</span>
<span class="token comment">// This will give us a direction to calculate the</span>
<span class="token comment">// tangent and bitangent</span>
<span class="token keyword">let</span> delta_uv1 <span class="token operator">=</span> uv1 <span class="token operator">-</span> uv0<span class="token punctuation">;</span>
<span class="token keyword">let</span> delta_uv2 <span class="token operator">=</span> uv2 <span class="token operator">-</span> uv0<span class="token punctuation">;</span>
<span class="token comment">// Solving the following system of equations will</span>
<span class="token comment">// give us the tangent and bitangent.</span>
<span class="token comment">// delta_pos1 = delta_uv1.x * T + delta_u.y * B</span>
<span class="token comment">// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B</span>
<span class="token comment">// Luckily, the place I found this equation provided</span>
<span class="token comment">// the solution!</span>
<span class="token keyword">let</span> r <span class="token operator">=</span> <span class="token number">1.0</span> <span class="token operator">/</span> <span class="token punctuation">(</span>delta_uv1<span class="token punctuation">.</span>x <span class="token operator">*</span> delta_uv2<span class="token punctuation">.</span>y <span class="token operator">-</span> delta_uv1<span class="token punctuation">.</span>y <span class="token operator">*</span> delta_uv2<span class="token punctuation">.</span>x<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> tangent <span class="token operator">=</span> <span class="token punctuation">(</span>delta_pos1 <span class="token operator">*</span> delta_uv2<span class="token punctuation">.</span>y <span class="token operator">-</span> delta_pos2 <span class="token operator">*</span> delta_uv1<span class="token punctuation">.</span>y<span class="token punctuation">)</span> <span class="token operator">*</span> r<span class="token punctuation">;</span>
<span class="token comment">// We flip the bitangent to enable right-handed normal</span>
<span class="token comment">// maps with wgpu texture coordinate system</span>
<span class="token keyword">let</span> bitangent <span class="token operator">=</span> <span class="token punctuation">(</span>delta_pos2 <span class="token operator">*</span> delta_uv1<span class="token punctuation">.</span>x <span class="token operator">-</span> delta_pos1 <span class="token operator">*</span> delta_uv2<span class="token punctuation">.</span>x<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token operator">-</span>r<span class="token punctuation">;</span>
<span class="token comment">// We'll use the same tangent/bitangent for each vertex in the triangle</span>
vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>tangent <span class="token operator">=</span>
<span class="token punctuation">(</span>tangent <span class="token operator">+</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">from</span><span class="token punctuation">(</span>vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>tangent<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>
vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>tangent <span class="token operator">=</span>
<span class="token punctuation">(</span>tangent <span class="token operator">+</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">from</span><span class="token punctuation">(</span>vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>tangent<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>
vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>tangent <span class="token operator">=</span>
<span class="token punctuation">(</span>tangent <span class="token operator">+</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">from</span><span class="token punctuation">(</span>vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>tangent<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>
vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>bitangent <span class="token operator">=</span>
<span class="token punctuation">(</span>bitangent <span class="token operator">+</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">from</span><span class="token punctuation">(</span>vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>bitangent<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>
vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>bitangent <span class="token operator">=</span>
<span class="token punctuation">(</span>bitangent <span class="token operator">+</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">from</span><span class="token punctuation">(</span>vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>bitangent<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>
vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>bitangent <span class="token operator">=</span>
<span class="token punctuation">(</span>bitangent <span class="token operator">+</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">from</span><span class="token punctuation">(</span>vertices<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span><span class="token punctuation">.</span>bitangent<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 comment">// Used to average the tangents/bitangents</span>
triangles_included<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1</span><span class="token punctuation">;</span>
triangles_included<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1</span><span class="token punctuation">;</span>
triangles_included<span class="token punctuation">[</span>c<span class="token punctuation">[</span><span class="token number">2</span><span class="token punctuation">]</span> <span class="token keyword">as</span> <span class="token keyword">usize</span><span class="token punctuation">]</span> <span class="token operator">+=</span> <span class="token number">1</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// Average the tangents/bitangents</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span>i<span class="token punctuation">,</span> n<span class="token punctuation">)</span> <span class="token keyword">in</span> triangles_included<span class="token punctuation">.</span><span class="token function">into_iter</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">enumerate</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> denom <span class="token operator">=</span> <span class="token number">1.0</span> <span class="token operator">/</span> n <span class="token keyword">as</span> <span class="token keyword">f32</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> v <span class="token operator">=</span> <span class="token operator">&amp;</span><span class="token keyword">mut</span> vertices<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
v<span class="token punctuation">.</span>tangent <span class="token operator">=</span> <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">from</span><span class="token punctuation">(</span>v<span class="token punctuation">.</span>tangent<span class="token punctuation">)</span> <span class="token operator">*</span> denom<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>
v<span class="token punctuation">.</span>bitangent <span class="token operator">=</span> <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">from</span><span class="token punctuation">(</span>v<span class="token punctuation">.</span>bitangent<span class="token punctuation">)</span> <span class="token operator">*</span> denom<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 keyword">let</span> vertex_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">&amp;</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 operator">&amp;</span><span class="token macro property">format!</span><span class="token punctuation">(</span><span class="token string">&quot;{:?} Vertex Buffer&quot;</span><span class="token punctuation">,</span> file_name<span class="token punctuation">)</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">&amp;</span>vertices<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">BufferUsages</span><span class="token punctuation">::</span><span class="token constant">VERTEX</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">let</span> index_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">&amp;</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 operator">&amp;</span><span class="token macro property">format!</span><span class="token punctuation">(</span><span class="token string">&quot;{:?} Index Buffer&quot;</span><span class="token punctuation">,</span> file_name<span class="token punctuation">)</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">&amp;</span>m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>indices<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">BufferUsages</span><span class="token punctuation">::</span><span class="token constant">INDEX</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">model<span class="token punctuation">::</span></span><span class="token class-name">Mesh</span> <span class="token punctuation">{</span>
name<span class="token punctuation">:</span> file_name<span class="token punctuation">.</span><span class="token function">to_string</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
vertex_buffer<span class="token punctuation">,</span>
index_buffer<span class="token punctuation">,</span>
num_elements<span class="token punctuation">:</span> m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>indices<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">u32</span><span class="token punctuation">,</span>
material<span class="token punctuation">:</span> m<span class="token punctuation">.</span>mesh<span class="token punctuation">.</span>material_id<span class="token punctuation">.</span><span class="token function">unwrap_or</span><span class="token punctuation">(</span><span class="token number">0</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 function">collect</span><span class="token punctuation">::</span><span class="token operator">&lt;</span><span class="token class-name">Vec</span><span class="token operator">&lt;</span>_<span class="token operator">&gt;&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h2 id="world-space-to-tangent-space"><a href="#world-space-to-tangent-space" class="header-anchor">#</a> World Space to Tangent Space</h2> <p>Since the normal map by default is in tangent space, we need to transform all the other variables used in that calculation to tangent space as well. We'll need to construct the tangent matrix in the vertex shader. First, we need our <code>VertexInput</code> to include the tangent and bitangents we calculated earlier.</p> <div class="language-wgsl extra-class"><pre class="language-wgsl"><code><span class="token keyword">struct</span> <span class="token class-name">VertexInput</span> <span class="token punctuation">{</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span> position<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">1</span><span class="token punctuation">)</span> tex_coords<span class="token punctuation">:</span> <span class="token builtin">vec2</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">2</span><span class="token punctuation">)</span> normal<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">3</span><span class="token punctuation">)</span> tangent<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">4</span><span class="token punctuation">)</span> bitangent<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>Next, we'll construct the <code>tangent_matrix</code> and then transform the vertex's light and view position into tangent space.</p> <div class="language-wgsl extra-class"><pre class="language-wgsl"><code><span class="token keyword">struct</span> <span class="token class-name">VertexOutput</span> <span class="token punctuation">{</span>
<span class="token punctuation">@</span><span class="token builtin-attribute"><span class="token attribute attr-name">builtin</span><span class="token punctuation">(</span><span class="token built-in-values attr-value">position</span><span class="token punctuation">)</span></span> clip_position<span class="token punctuation">:</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span> tex_coords<span class="token punctuation">:</span> <span class="token builtin">vec2</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token comment">// UPDATED!</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">1</span><span class="token punctuation">)</span> tangent_position<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">2</span><span class="token punctuation">)</span> tangent_light_position<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">3</span><span class="token punctuation">)</span> tangent_view_position<span class="token punctuation">:</span> <span class="token builtin">vec3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token punctuation">@</span><span class="token attributes attr-name">vertex</span>
<span class="token keyword">fn</span> <span class="token functions function">vs_main</span><span class="token punctuation">(</span>
model<span class="token punctuation">:</span> <span class="token class-name">VertexInput</span><span class="token punctuation">,</span>
instance<span class="token punctuation">:</span> <span class="token class-name">InstanceInput</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token class-name">VertexOutput</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token keyword">let</span> normal_matrix <span class="token operator">=</span> <span class="token builtin">mat3x3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">(</span>
instance<span class="token punctuation">.</span>normal_matrix_0<span class="token punctuation">,</span>
instance<span class="token punctuation">.</span>normal_matrix_1<span class="token punctuation">,</span>
instance<span class="token punctuation">.</span>normal_matrix_2<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Construct the tangent matrix</span>
<span class="token keyword">let</span> world_normal <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>normal_matrix <span class="token operator">*</span> model<span class="token punctuation">.</span>normal<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> world_tangent <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>normal_matrix <span class="token operator">*</span> model<span class="token punctuation">.</span>tangent<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> world_bitangent <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>normal_matrix <span class="token operator">*</span> model<span class="token punctuation">.</span>bitangent<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> tangent_matrix <span class="token operator">=</span> <span class="token builtin">transpose</span><span class="token punctuation">(</span><span class="token builtin">mat3x3</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">(</span>
world_tangent<span class="token punctuation">,</span>
world_bitangent<span class="token punctuation">,</span>
world_normal<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> world_position <span class="token operator">=</span> model_matrix <span class="token operator">*</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span><span class="token punctuation">(</span>model<span class="token punctuation">.</span>position<span class="token punctuation">,</span> <span class="token decimal-float-literal number">1.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">var</span> out<span class="token punctuation">:</span> <span class="token class-name">VertexOutput</span><span class="token punctuation">;</span>
out<span class="token punctuation">.</span>clip_position <span class="token operator">=</span> camera<span class="token punctuation">.</span>view_proj <span class="token operator">*</span> world_position<span class="token punctuation">;</span>
out<span class="token punctuation">.</span>tex_coords <span class="token operator">=</span> model<span class="token punctuation">.</span>tex_coords<span class="token punctuation">;</span>
out<span class="token punctuation">.</span>tangent_position <span class="token operator">=</span> tangent_matrix <span class="token operator">*</span> world_position<span class="token punctuation">.</span>xyz<span class="token punctuation">;</span>
out<span class="token punctuation">.</span>tangent_view_position <span class="token operator">=</span> tangent_matrix <span class="token operator">*</span> camera<span class="token punctuation">.</span>view_pos<span class="token punctuation">.</span>xyz<span class="token punctuation">;</span>
out<span class="token punctuation">.</span>tangent_light_position <span class="token operator">=</span> tangent_matrix <span class="token operator">*</span> light<span class="token punctuation">.</span>position<span class="token punctuation">;</span>
<span class="token keyword">return</span> out<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Finally, we'll update the fragment shader to use these transformed lighting values.</p> <div class="language-wgsl extra-class"><pre class="language-wgsl"><code><span class="token punctuation">@</span><span class="token attributes attr-name">fragment</span>
<span class="token keyword">fn</span> <span class="token functions function">fs_main</span><span class="token punctuation">(</span>in<span class="token punctuation">:</span> <span class="token class-name">VertexOutput</span><span class="token punctuation">)</span> <span class="token operator">-&gt;</span> <span class="token punctuation">@</span><span class="token attributes attr-name">location</span><span class="token punctuation">(</span><span class="token int-literal number">0</span><span class="token punctuation">)</span> <span class="token builtin">vec4</span><span class="token punctuation">&lt;</span><span class="token builtin">f32</span><span class="token punctuation">&gt;</span> <span class="token punctuation">{</span>
<span class="token comment">// Sample textures..</span>
<span class="token comment">// Create the lighting vectors</span>
<span class="token keyword">let</span> tangent_normal <span class="token operator">=</span> object_normal<span class="token punctuation">.</span>xyz <span class="token operator">*</span> <span class="token decimal-float-literal number">2.0</span> <span class="token operator">-</span> <span class="token decimal-float-literal number">1.0</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> light_dir <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>in<span class="token punctuation">.</span>tangent_light_position <span class="token operator">-</span> in<span class="token punctuation">.</span>tangent_position<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> view_dir <span class="token operator">=</span> <span class="token builtin">normalize</span><span class="token punctuation">(</span>in<span class="token punctuation">.</span>tangent_view_position <span class="token operator">-</span> in<span class="token punctuation">.</span>tangent_position<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// Perform lighting calculations...</span>
<span class="token punctuation">}</span>
</code></pre></div><p>We get the following from this calculation.</p> <p><img src="/learn-wgpu/assets/img/normal_mapping_correct.c1f96315.png" alt=""></p> <h2 id="srgb-and-normal-textures"><a href="#srgb-and-normal-textures" class="header-anchor">#</a> Srgb and normal textures</h2> <p>We've been using <code>Rgba8UnormSrgb</code> for all our textures. The <code>Srgb</code> bit specifies that we will be using <a href="https://en.wikipedia.org/wiki/SRGB" target="_blank" rel="noopener noreferrer">standard red green blue color space<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" 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> <span class="sr-only">(opens new window)</span></span></a>. This is also known as linear color space. Linear color space has less color density. Even so, it is often used for diffuse textures, as they are typically made in <code>Srgb</code> color space.</p> <p>Normal textures aren't made with <code>Srgb</code>. Using <code>Rgba8UnormSrgb</code> can change how the GPU samples the texture. This can make the resulting simulation <a href="https://medium.com/@bgolus/generating-perfect-normal-maps-for-unity-f929e673fc57#b86c" target="_blank" rel="noopener noreferrer">less accurate<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" 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> <span class="sr-only">(opens new window)</span></span></a>. We can avoid these issues by using <code>Rgba8Unorm</code> when we create the texture. Let's add an <code>is_normal_map</code> method to our <code>Texture</code> struct.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">from_image</span><span class="token punctuation">(</span>
device<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Device</span><span class="token punctuation">,</span>
queue<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Queue</span><span class="token punctuation">,</span>
img<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">image<span class="token punctuation">::</span></span><span class="token class-name">DynamicImage</span><span class="token punctuation">,</span>
label<span class="token punctuation">:</span> <span class="token class-name">Option</span><span class="token operator">&lt;</span><span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token operator">&gt;</span><span class="token punctuation">,</span>
is_normal_map<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span> <span class="token comment">// NEW!</span>
<span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">Self</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token comment">// NEW!</span>
<span class="token keyword">let</span> format <span class="token operator">=</span> <span class="token keyword">if</span> is_normal_map <span class="token punctuation">{</span>
<span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureFormat</span><span class="token punctuation">::</span><span class="token class-name">Rgba8Unorm</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureFormat</span><span class="token punctuation">::</span><span class="token class-name">Rgba8UnormSrgb</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> texture <span class="token operator">=</span> device<span class="token punctuation">.</span><span class="token function">create_texture</span><span class="token punctuation">(</span><span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureDescriptor</span> <span class="token punctuation">{</span>
label<span class="token punctuation">,</span>
size<span class="token punctuation">,</span>
mip_level_count<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
sample_count<span class="token punctuation">:</span> <span class="token number">1</span><span class="token punctuation">,</span>
dimension<span class="token punctuation">:</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureDimension</span><span class="token punctuation">::</span><span class="token constant">D2</span><span class="token punctuation">,</span>
<span class="token comment">// UPDATED!</span>
format<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">TextureUsages</span><span class="token punctuation">::</span><span class="token constant">TEXTURE_BINDING</span> <span class="token operator">|</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">TextureUsages</span><span class="token punctuation">::</span><span class="token constant">COPY_DST</span><span class="token punctuation">,</span>
view_formats<span class="token punctuation">:</span> <span class="token operator">&amp;</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 comment">// ...</span>
<span class="token class-name">Ok</span><span class="token punctuation">(</span><span class="token keyword">Self</span> <span class="token punctuation">{</span>
texture<span class="token punctuation">,</span>
view<span class="token punctuation">,</span>
sampler<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
</code></pre></div><p>We'll need to propagate this change to the other methods that use this.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">fn</span> <span class="token function-definition function">from_bytes</span><span class="token punctuation">(</span>
device<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Device</span><span class="token punctuation">,</span>
queue<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Queue</span><span class="token punctuation">,</span>
bytes<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token punctuation">[</span><span class="token keyword">u8</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
label<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span>
is_normal_map<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span> <span class="token comment">// NEW!</span>
<span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">Self</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> img <span class="token operator">=</span> <span class="token namespace">image<span class="token punctuation">::</span></span><span class="token function">load_from_memory</span><span class="token punctuation">(</span>bytes<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><span class="token function">from_image</span><span class="token punctuation">(</span>device<span class="token punctuation">,</span> queue<span class="token punctuation">,</span> <span class="token operator">&amp;</span>img<span class="token punctuation">,</span> <span class="token class-name">Some</span><span class="token punctuation">(</span>label<span class="token punctuation">)</span><span class="token punctuation">,</span> is_normal_map<span class="token punctuation">)</span> <span class="token comment">// UPDATED!</span>
<span class="token punctuation">}</span>
</code></pre></div><p>We need to update <code>resource.rs</code> as well.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">load_texture</span><span class="token punctuation">(</span>
file_name<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span>
is_normal_map<span class="token punctuation">:</span> <span class="token keyword">bool</span><span class="token punctuation">,</span>
device<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Device</span><span class="token punctuation">,</span>
queue<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Queue</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token namespace">anyhow<span class="token punctuation">::</span></span><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token function">load_binary</span><span class="token punctuation">(</span>file_name<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span>
<span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">::</span><span class="token function">from_bytes</span><span class="token punctuation">(</span>device<span class="token punctuation">,</span> queue<span class="token punctuation">,</span> <span class="token operator">&amp;</span>data<span class="token punctuation">,</span> file_name<span class="token punctuation">,</span> is_normal_map<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token keyword">pub</span> <span class="token keyword">async</span> <span class="token keyword">fn</span> <span class="token function-definition function">load_model</span><span class="token punctuation">(</span>
file_name<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token keyword">str</span><span class="token punctuation">,</span>
device<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Device</span><span class="token punctuation">,</span>
queue<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">Queue</span><span class="token punctuation">,</span>
layout<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroupLayout</span><span class="token punctuation">,</span>
<span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token namespace">anyhow<span class="token punctuation">::</span></span><span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token namespace">model<span class="token punctuation">::</span></span><span class="token class-name">Model</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token keyword">let</span> <span class="token keyword">mut</span> materials <span class="token operator">=</span> <span class="token class-name">Vec</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>
<span class="token keyword">for</span> m <span class="token keyword">in</span> obj_materials<span class="token operator">?</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> diffuse_texture <span class="token operator">=</span> <span class="token function">load_texture</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>m<span class="token punctuation">.</span>diffuse_texture<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">,</span> device<span class="token punctuation">,</span> queue<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span> <span class="token comment">// UDPATED!</span>
<span class="token keyword">let</span> normal_texture <span class="token operator">=</span> <span class="token function">load_texture</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>m<span class="token punctuation">.</span>normal_texture<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> device<span class="token punctuation">,</span> queue<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token keyword">await</span><span class="token operator">?</span><span class="token punctuation">;</span> <span class="token comment">// UPDATED!</span>
materials<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token namespace">model<span class="token punctuation">::</span></span><span class="token class-name">Material</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span>
device<span class="token punctuation">,</span>
<span class="token operator">&amp;</span>m<span class="token punctuation">.</span>name<span class="token punctuation">,</span>
diffuse_texture<span class="token punctuation">,</span>
normal_texture<span class="token punctuation">,</span>
layout<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>That gives us the following.</p> <p><img src="/learn-wgpu/assets/img/no_srgb.2f9f1450.png" alt=""></p> <h2 id="unrelated-stuff"><a href="#unrelated-stuff" class="header-anchor">#</a> Unrelated stuff</h2> <p>I wanted to mess around with other materials so I added a <code>draw_model_instanced_with_material()</code> to the <code>DrawModel</code> trait.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">pub</span> <span class="token keyword">trait</span> <span class="token type-definition class-name">DrawModel</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token keyword">fn</span> <span class="token function-definition function">draw_model_instanced_with_material</span><span class="token punctuation">(</span>
<span class="token operator">&amp;</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">,</span>
model<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token class-name">Model</span><span class="token punctuation">,</span>
material<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token class-name">Material</span><span class="token punctuation">,</span>
instances<span class="token punctuation">:</span> <span class="token class-name">Range</span><span class="token operator">&lt;</span><span class="token keyword">u32</span><span class="token operator">&gt;</span><span class="token punctuation">,</span>
camera_bind_group<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroup</span><span class="token punctuation">,</span>
light_bind_group<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'a</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 punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">impl</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token punctuation">,</span> <span class="token lifetime-annotation symbol">'b</span><span class="token operator">&gt;</span> <span class="token class-name">DrawModel</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'b</span><span class="token operator">&gt;</span> <span class="token keyword">for</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">RenderPass</span><span class="token operator">&lt;</span><span class="token lifetime-annotation symbol">'a</span><span class="token operator">&gt;</span>
<span class="token keyword">where</span>
<span class="token lifetime-annotation symbol">'b</span><span class="token punctuation">:</span> <span class="token lifetime-annotation symbol">'a</span><span class="token punctuation">,</span>
<span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token keyword">fn</span> <span class="token function-definition function">draw_model_instanced_with_material</span><span class="token punctuation">(</span>
<span class="token operator">&amp;</span><span class="token keyword">mut</span> <span class="token keyword">self</span><span class="token punctuation">,</span>
model<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'b</span> <span class="token class-name">Model</span><span class="token punctuation">,</span>
material<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'b</span> <span class="token class-name">Material</span><span class="token punctuation">,</span>
instances<span class="token punctuation">:</span> <span class="token class-name">Range</span><span class="token operator">&lt;</span><span class="token keyword">u32</span><span class="token operator">&gt;</span><span class="token punctuation">,</span>
camera_bind_group<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'b</span> <span class="token namespace">wgpu<span class="token punctuation">::</span></span><span class="token class-name">BindGroup</span><span class="token punctuation">,</span>
light_bind_group<span class="token punctuation">:</span> <span class="token operator">&amp;</span><span class="token lifetime-annotation symbol">'b</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 punctuation">{</span>
<span class="token keyword">for</span> mesh <span class="token keyword">in</span> <span class="token operator">&amp;</span>model<span class="token punctuation">.</span>meshes <span class="token punctuation">{</span>
<span class="token keyword">self</span><span class="token punctuation">.</span><span class="token function">draw_mesh_instanced</span><span class="token punctuation">(</span>mesh<span class="token punctuation">,</span> material<span class="token punctuation">,</span> instances<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> camera_bind_group<span class="token punctuation">,</span> light_bind_group<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>I found a cobblestone texture with a matching normal map and created a <code>debug_material</code> for that.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token comment">// lib.rs</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">&amp;</span><span class="token class-name">Window</span><span class="token punctuation">)</span> <span class="token punctuation">-&gt;</span> <span class="token class-name">Result</span><span class="token operator">&lt;</span><span class="token keyword">Self</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
<span class="token comment">// ...</span>
<span class="token keyword">let</span> debug_material <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> diffuse_bytes <span class="token operator">=</span> <span class="token macro property">include_bytes!</span><span class="token punctuation">(</span><span class="token string">&quot;../res/cobble-diffuse.png&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> normal_bytes <span class="token operator">=</span> <span class="token macro property">include_bytes!</span><span class="token punctuation">(</span><span class="token string">&quot;../res/cobble-normal.png&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> diffuse_texture <span class="token operator">=</span> <span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">::</span><span class="token function">from_bytes</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>device<span class="token punctuation">,</span> <span class="token operator">&amp;</span>queue<span class="token punctuation">,</span> diffuse_bytes<span class="token punctuation">,</span> <span class="token string">&quot;res/alt-diffuse.png&quot;</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> normal_texture <span class="token operator">=</span> <span class="token namespace">texture<span class="token punctuation">::</span></span><span class="token class-name">Texture</span><span class="token punctuation">::</span><span class="token function">from_bytes</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>device<span class="token punctuation">,</span> <span class="token operator">&amp;</span>queue<span class="token punctuation">,</span> normal_bytes<span class="token punctuation">,</span> <span class="token string">&quot;res/alt-normal.png&quot;</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">unwrap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token namespace">model<span class="token punctuation">::</span></span><span class="token class-name">Material</span><span class="token punctuation">::</span><span class="token function">new</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>device<span class="token punctuation">,</span> <span class="token string">&quot;alt-material&quot;</span><span class="token punctuation">,</span> diffuse_texture<span class="token punctuation">,</span> normal_texture<span class="token punctuation">,</span> <span class="token operator">&amp;</span>texture_bind_group_layout<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>
<span class="token attribute attr-name">#[allow(dead_code)]</span>
debug_material<span class="token punctuation">,</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>Then to render with the <code>debug_material</code> I used the <code>draw_model_instanced_with_material()</code> that I created.</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">&amp;</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">draw_model_instanced_with_material</span><span class="token punctuation">(</span>
<span class="token operator">&amp;</span><span class="token keyword">self</span><span class="token punctuation">.</span>obj_model<span class="token punctuation">,</span>
<span class="token operator">&amp;</span><span class="token keyword">self</span><span class="token punctuation">.</span>debug_material<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>instances<span class="token punctuation">.</span><span class="token function">len</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">u32</span><span class="token punctuation">,</span>
<span class="token operator">&amp;</span><span class="token keyword">self</span><span class="token punctuation">.</span>camera_bind_group<span class="token punctuation">,</span>
<span class="token operator">&amp;</span><span class="token keyword">self</span><span class="token punctuation">.</span>light_bind_group<span class="token punctuation">,</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>That gives us something like this.</p> <p><img src="/learn-wgpu/assets/img/debug_material.b374492b.png" alt=""></p> <p>You can find the textures I use in the Github Repository.</p> <div id="wasm-example"><!----> <button>Try Tutorial11_normals!</button></div> <div class="auto-github-link"><a href="https://github.com/sotrh/learn-wgpu/tree/master/code/intermediate/tutorial11-normals/" target="_blank" rel="noopener noreferrer">Check out the code!</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" 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> <span class="sr-only">(opens new window)</span></span></div></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">Last Updated: </span> <span class="time">11/27/2023, 10:42:27 PM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
<a href="/learn-wgpu/intermediate/tutorial10-lighting/" class="prev">
Working with Lights
</a></span> <span class="next"><a href="/learn-wgpu/intermediate/tutorial12-camera/">
A Better Camera
</a>
</span></p></div> </main></div></div><div class="global-ui"><!----></div></div>
<script src="/learn-wgpu/assets/js/app.8ee4646c.js" defer></script><script src="/learn-wgpu/assets/js/2.2ef7287a.js" defer></script><script src="/learn-wgpu/assets/js/1.eaeeb819.js" defer></script><script src="/learn-wgpu/assets/js/26.01a5696a.js" defer></script><script src="/learn-wgpu/assets/js/33.945dce62.js" defer></script><script src="/learn-wgpu/assets/js/37.5ecf7ce2.js" defer></script>
</body>
</html>