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/showcase/compute/index.html

128 lines
34 KiB
HTML

<!DOCTYPE html>
<html lang="en-US">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Compute Example: Tangents and Bitangents | 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.a49faced.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/32.e6627282.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.dd15c417.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.a01d50ae.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/26.fd4eb8d5.js"><link rel="prefetch" href="/learn-wgpu/assets/js/27.914a9a40.js"><link rel="prefetch" href="/learn-wgpu/assets/js/28.77f7d987.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.c58683af.js"><link rel="prefetch" href="/learn-wgpu/assets/js/31.74bc1dea.js"><link rel="prefetch" href="/learn-wgpu/assets/js/33.945dce62.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.e6a8b391.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.47515217.js"><link rel="prefetch" href="/learn-wgpu/assets/js/42.4caac867.js"><link rel="prefetch" href="/learn-wgpu/assets/js/43.ac6fc643.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.e591bcb9.js"><link rel="prefetch" href="/learn-wgpu/assets/js/47.3a7bf317.js"><link rel="prefetch" href="/learn-wgpu/assets/js/48.0a15e622.js"><link rel="prefetch" href="/learn-wgpu/assets/js/49.ad5a99aa.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.820d6108.js"><link rel="prefetch" href="/learn-wgpu/assets/js/52.48268452.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.a43d5d8e.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.24bfa87e.js"><link rel="prefetch" href="/learn-wgpu/assets/js/63.90f94cac.js"><link rel="prefetch" href="/learn-wgpu/assets/js/64.b9a310af.js"><link rel="prefetch" href="/learn-wgpu/assets/js/65.362df98e.js"><link rel="prefetch" href="/learn-wgpu/assets/js/66.ef1e3716.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"><span>Intermediate</span> <!----></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/learn-wgpu/intermediate/tutorial10-lighting/" class="sidebar-link">Working with Lights</a></li><li><a href="/learn-wgpu/intermediate/tutorial11-normals/" class="sidebar-link">Normal Mapping</a></li><li><a href="/learn-wgpu/intermediate/tutorial12-camera/" class="sidebar-link">A Better Camera</a></li><li><a href="/learn-wgpu/intermediate/tutorial13-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 open"><span>Showcase</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/learn-wgpu/showcase/" aria-current="page" class="sidebar-link">Foreword</a></li><li><a href="/learn-wgpu/showcase/windowless/" class="sidebar-link">Wgpu without a window</a></li><li><a href="/learn-wgpu/showcase/gifs/" class="sidebar-link">Creating gifs</a></li><li><a href="/learn-wgpu/showcase/pong/" class="sidebar-link">Pong</a></li><li><a href="/learn-wgpu/showcase/compute/" aria-current="page" class="active sidebar-link">Compute Example: Tangents and Bitangents</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/learn-wgpu/showcase/compute/#possible-improvements" class="sidebar-link">Possible Improvements</a></li><li class="sidebar-sub-header"><a href="/learn-wgpu/showcase/compute/#results" class="sidebar-link">Results</a></li></ul></li><li><a href="/learn-wgpu/showcase/alignment/" class="sidebar-link">Memory Layout in WGSL</a></li></ul></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="compute-example-tangents-and-bitangents"><a href="#compute-example-tangents-and-bitangents" class="header-anchor">#</a> Compute Example: Tangents and Bitangents</h1> <p>This proved more difficult than I anticipated. The first problem I encountered was some vertex data corruption due to the shader reading my vertex data incorrectly. I was using the <code>ModelVertex</code> struct I used in the <a href="/learn-wgpu/intermediate/tutorial11-normals/">normal mapping tutorial</a>.</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>
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>
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>
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>
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>
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>This structure works perfectly fine when used as a vertex buffer. Using it as a storage buffer proved less convenient. My previous code used a GLSL struct similar to my <code>ModelVertex</code>.</p> <div class="language-glsl extra-class"><pre class="language-glsl"><code><span class="token keyword">struct</span> <span class="token class-name">ModelVertex</span> <span class="token punctuation">{</span>
<span class="token keyword">vec3</span> position<span class="token punctuation">;</span>
<span class="token keyword">vec2</span> tex_coords<span class="token punctuation">;</span>
<span class="token keyword">vec3</span> normal<span class="token punctuation">;</span>
<span class="token keyword">vec3</span> tangent<span class="token punctuation">;</span>
<span class="token keyword">vec3</span> bitangent<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>At first glance, this seems just fine, but OpenGL experts would likely see a problem with the structure. Our fields aren't aligned properly to support the <code>std430</code> alignment that storage buffers require. I won't get into detail but you can check out the <a href="../alignment">alignment showcase</a> if you want to know more. To summarize, the <code>vec2</code> for the <code>tex_coords</code> was messing up the byte alignment, corrupting the vertex data resulting in the following:</p> <p><img src="/learn-wgpu/assets/img/corruption.675b1eca.png" alt="./corruption.png"></p> <p>I could have fixed this by adding a padding field after <code>tex_coords</code> on the Rust side, but that would require modifying the <code>VertexBufferLayout</code>. I ended up solving this problem by using the components of the vectors directly which resulted in a struct like this:</p> <div class="language-glsl extra-class"><pre class="language-glsl"><code><span class="token keyword">struct</span> <span class="token class-name">ModelVertex</span> <span class="token punctuation">{</span>
<span class="token keyword">float</span> x<span class="token punctuation">;</span> <span class="token keyword">float</span> y<span class="token punctuation">;</span> <span class="token keyword">float</span> z<span class="token punctuation">;</span>
<span class="token keyword">float</span> uv<span class="token punctuation">;</span> <span class="token keyword">float</span> uw<span class="token punctuation">;</span>
<span class="token keyword">float</span> nx<span class="token punctuation">;</span> <span class="token keyword">float</span> ny<span class="token punctuation">;</span> <span class="token keyword">float</span> nz<span class="token punctuation">;</span>
<span class="token keyword">float</span> tx<span class="token punctuation">;</span> <span class="token keyword">float</span> ty<span class="token punctuation">;</span> <span class="token keyword">float</span> tz<span class="token punctuation">;</span>
<span class="token keyword">float</span> bx<span class="token punctuation">;</span> <span class="token keyword">float</span> by<span class="token punctuation">;</span> <span class="token keyword">float</span> bz<span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
</code></pre></div><p>Since <code>std430</code> will use the alignment of the largest element of the struct, using all floats means the struct will be aligned to 4 bytes. This is alignment matches what <code>ModelVertex</code> uses in Rust. This was kind of a pain to work with, but it fixed the corruption issue.</p> <p>The second problem required me to rethink how I was computing the tangent and bitangent. The previous algorithm I was using only computed the tangent and bitangent for each triangle and set all the vertices in that triangle to use the same tangent and bitangent. While this is fine in a single-threaded context, the code breaks down when trying to compute the triangles in parallel. The reason is that multiple triangles can share the same vertices. This means that when we go to save the resulting tangents, we inevitably end up trying to write to the same vertex from multiple different threads which is a big no no. You can see the issue with this method below:</p> <p><img src="/learn-wgpu/assets/img/black_triangles.df338b97.png" alt="./black_triangles.png"></p> <p>Those black triangles were the result of multiple GPU threads trying to modify the same vertices. Looking at the data in Render Doc I could see that the tangents and bitangents were garbage numbers such as <code>NaN</code>.</p> <p><img src="/learn-wgpu/assets/img/render_doc_output.e0c8b298.png" alt="./render_doc_output.png"></p> <p>While on the CPU we could introduce a synchronization primitive such as a <code>Mutex</code> to fix this issue, AFAIK there isn't really such a thing on the GPU. Instead, I decided to swap my code to work with each vertex individually. There are some hurdles with that, but those will be easier to explain in code. Let's start with the <code>main</code> function.</p> <div class="language-glsl extra-class"><pre class="language-glsl"><code><span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">uint</span> vertexIndex <span class="token operator">=</span> gl_GlobalInvocationID<span class="token punctuation">.</span>x<span class="token punctuation">;</span>
ModelVertex result <span class="token operator">=</span> <span class="token function">calcTangentBitangent</span><span class="token punctuation">(</span>vertexIndex<span class="token punctuation">)</span><span class="token punctuation">;</span>
dstVertices<span class="token punctuation">[</span>vertexIndex<span class="token punctuation">]</span> <span class="token operator">=</span> result<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>We use the <code>gl_GlobalInvocationID.x</code> to get the index of the vertex we want to compute the tangents for. I opted to put the actual calculation into its own method. Let's take a look at that.</p> <div class="language-glsl extra-class"><pre class="language-glsl"><code>ModelVertex <span class="token function">calcTangentBitangent</span><span class="token punctuation">(</span><span class="token keyword">uint</span> vertexIndex<span class="token punctuation">)</span> <span class="token punctuation">{</span>
ModelVertex v <span class="token operator">=</span> srcVertices<span class="token punctuation">[</span>vertexIndex<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">vec3</span> tangent <span class="token operator">=</span> <span class="token keyword">vec3</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 keyword">vec3</span> bitangent <span class="token operator">=</span> <span class="token keyword">vec3</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 keyword">uint</span> trianglesIncluded <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token comment">// Find the triangles that use v</span>
<span class="token comment">// * Loop over every triangle (i + 3)</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">uint</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> numIndices<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>
<span class="token keyword">uint</span> index0 <span class="token operator">=</span> indices<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">uint</span> index1 <span class="token operator">=</span> indices<span class="token punctuation">[</span>i<span class="token operator">+</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">uint</span> index2 <span class="token operator">=</span> indices<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>
<span class="token comment">// Only perform the calculation if one of the indices</span>
<span class="token comment">// matches our vertexIndex</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>index0 <span class="token operator">==</span> vertexIndex <span class="token operator">||</span> index1 <span class="token operator">==</span> vertexIndex <span class="token operator">||</span> index2 <span class="token operator">==</span> vertexIndex<span class="token punctuation">)</span> <span class="token punctuation">{</span>
ModelVertex v0 <span class="token operator">=</span> srcVertices<span class="token punctuation">[</span>index0<span class="token punctuation">]</span><span class="token punctuation">;</span>
ModelVertex v1 <span class="token operator">=</span> srcVertices<span class="token punctuation">[</span>index1<span class="token punctuation">]</span><span class="token punctuation">;</span>
ModelVertex v2 <span class="token operator">=</span> srcVertices<span class="token punctuation">[</span>index2<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">vec3</span> pos0 <span class="token operator">=</span> <span class="token function">getPos</span><span class="token punctuation">(</span>v0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">vec3</span> pos1 <span class="token operator">=</span> <span class="token function">getPos</span><span class="token punctuation">(</span>v1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">vec3</span> pos2 <span class="token operator">=</span> <span class="token function">getPos</span><span class="token punctuation">(</span>v2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">vec2</span> uv0 <span class="token operator">=</span> <span class="token function">getUV</span><span class="token punctuation">(</span>v0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">vec2</span> uv1 <span class="token operator">=</span> <span class="token function">getUV</span><span class="token punctuation">(</span>v1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">vec2</span> uv2 <span class="token operator">=</span> <span class="token function">getUV</span><span class="token punctuation">(</span>v2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">vec3</span> delta_pos1 <span class="token operator">=</span> pos1 <span class="token operator">-</span> pos0<span class="token punctuation">;</span>
<span class="token keyword">vec3</span> delta_pos2 <span class="token operator">=</span> pos2 <span class="token operator">-</span> pos0<span class="token punctuation">;</span>
<span class="token keyword">vec2</span> delta_uv1 <span class="token operator">=</span> uv1 <span class="token operator">-</span> uv0<span class="token punctuation">;</span>
<span class="token keyword">vec2</span> delta_uv2 <span class="token operator">=</span> uv2 <span class="token operator">-</span> uv0<span class="token punctuation">;</span>
<span class="token keyword">float</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>
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>
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> r<span class="token punctuation">;</span>
trianglesIncluded <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>
<span class="token comment">// Average the tangent and bitangents</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>trianglesIncluded <span class="token operator">&gt;</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
tangent <span class="token operator">/=</span> trianglesIncluded<span class="token punctuation">;</span>
bitangent <span class="token operator">/=</span> trianglesIncluded<span class="token punctuation">;</span>
tangent <span class="token operator">=</span> <span class="token function">normalize</span><span class="token punctuation">(</span>tangent<span class="token punctuation">)</span><span class="token punctuation">;</span>
bitangent <span class="token operator">=</span> <span class="token function">normalize</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 comment">// Save the results</span>
v<span class="token punctuation">.</span>tx <span class="token operator">=</span> tangent<span class="token punctuation">.</span>x<span class="token punctuation">;</span>
v<span class="token punctuation">.</span>ty <span class="token operator">=</span> tangent<span class="token punctuation">.</span>y<span class="token punctuation">;</span>
v<span class="token punctuation">.</span>tz <span class="token operator">=</span> tangent<span class="token punctuation">.</span>z<span class="token punctuation">;</span>
v<span class="token punctuation">.</span>bx <span class="token operator">=</span> bitangent<span class="token punctuation">.</span>x<span class="token punctuation">;</span>
v<span class="token punctuation">.</span>by <span class="token operator">=</span> bitangent<span class="token punctuation">.</span>y<span class="token punctuation">;</span>
v<span class="token punctuation">.</span>bz <span class="token operator">=</span> bitangent<span class="token punctuation">.</span>z<span class="token punctuation">;</span>
<span class="token keyword">return</span> v<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><h2 id="possible-improvements"><a href="#possible-improvements" class="header-anchor">#</a> Possible Improvements</h2> <p>Looping over every triangle for every vertex is likely raising some red flags for some of you. In a single-threaded context, this algorithm would end up being O(N*M). As we are utilizing the high number of threads available to our GPU, this is less of an issue, but it still means our GPU is burning more cycles than it needs to.</p> <p>One way I came up with to possibly improve performance is to store the index of each triangle in a hash map like structure with the vertex index as keys. Here's some pseudo code:</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">for</span> t <span class="token keyword">in</span> <span class="token number">0</span><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 operator">/</span> <span class="token number">3</span> <span class="token punctuation">{</span>
triangle_map<span class="token punctuation">[</span>indices<span class="token punctuation">[</span>t <span class="token operator">*</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 function">push</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span>
triangle_map<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">(</span>indices<span class="token punctuation">[</span>t <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> t<span class="token punctuation">)</span><span class="token punctuation">;</span>
triangle_map<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">(</span>indices<span class="token punctuation">[</span>t <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> t<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>We'd then need to flatten this structure to pass it to the GPU. We'd also need a second array to index the first.</p> <div class="language-rust extra-class"><pre class="language-rust"><code><span class="token keyword">for</span> <span class="token punctuation">(</span>i<span class="token punctuation">,</span> <span class="token punctuation">(</span>_v<span class="token punctuation">,</span> t_list<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">in</span> triangle_map<span class="token punctuation">.</span><span class="token function">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>
triangle_map_indices<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token class-name">TriangleMapIndex</span> <span class="token punctuation">{</span>
start<span class="token punctuation">:</span> i<span class="token punctuation">,</span>
len<span class="token punctuation">:</span> t_list<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 punctuation">)</span><span class="token punctuation">;</span>
flat_triangle_map<span class="token punctuation">.</span><span class="token function">extend</span><span class="token punctuation">(</span>t_list<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre></div><p>I ultimately decided against this method as it was more complicated, and I haven't had time to benchmark it to see if it's faster than the simple method.</p> <h2 id="results"><a href="#results" class="header-anchor">#</a> Results</h2> <p>The tangents and bitangents are now getting calculated correctly and on the GPU!</p> <p><img src="/learn-wgpu/assets/img/results.7918efc1.png" alt="./results.png"></p> <div class="auto-github-link"><a href="https://github.com/sotrh/learn-wgpu/tree/master/code/showcase/compute/" 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/28/2023, 12:41:28 AM</span></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
<a href="/learn-wgpu/showcase/pong/" class="prev">
Pong
</a></span> <span class="next"><a href="/learn-wgpu/showcase/alignment/">
Memory Layout in WGSL
</a>
</span></p></div> </main></div></div><div class="global-ui"><!----></div></div>
<script src="/learn-wgpu/assets/js/app.a49faced.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/32.e6627282.js" defer></script><script src="/learn-wgpu/assets/js/37.5ecf7ce2.js" defer></script>
</body>
</html>