<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="/feed/project-feed.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="en">
	<title>Showcasing 3D CAD models Updates | ATOMWOLF</title>
	<subtitle>Updates for Showcasing 3D CAD models</subtitle>
	<link href="https://www.atomwolf.org/projects/showcasing-3d-cad-models/feed/feed.xml" rel="self"/>
	<link href="https://www.atomwolf.org/projects/showcasing-3d-cad-models/"/>
	<link href="https://www.atomwolf.org/"/>
	<updated>2024-07-18T00:00:00Z</updated>
	<id>/projects/showcasing-3d-cad-models/</id>
	<author>
		<name>Adam Wolf</name>
		<email>atomwolf@atomwolf.org</email>
	</author>
	<entry>
		<title>Rendering Outlines with a Post-processing Shader</title>
		<link href="https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/"/>
		<updated>2024-07-18T00:00:00Z</updated>
		<id>https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/</id>
		 <content type="html">
&lt;div class=&quot;dotted-metadata-block&quot;&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Summary&lt;/span&gt;: I learn about shaders to make a post-processing effect for model-viewer that adds outlines to highlight detail in 3D CAD models.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Assumed audience&lt;/span&gt;: curious folks on the web. You shouldn&#39;t need to know already know 3D graphics, shaders, or OpenGL.&lt;label for=&quot;sn-assumed-audience&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;&lt;input type=&quot;checkbox&quot; id=&quot;sn-assumed-audience&quot; class=&quot;margin-toggle&quot;&gt;&lt;small class=&quot;sidenote&quot;&gt;Don&#39;t lose heart! The world rewards curiosity.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Project&lt;/span&gt;: part of &lt;a href=&quot;https://www.atomwolf.org/projects/showcasing-3d-cad-models/&quot;&gt;Showcasing 3D CAD Models on the Web&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;span class=&quot;small-caps&quot;&gt;Tags&lt;/span&gt;:
		&lt;a href=&quot;https://www.atomwolf.org/tags/3d-graphics/&quot; class=&quot;entry-tag&quot;&gt;3D graphics&lt;/a&gt;, 
		&lt;a href=&quot;https://www.atomwolf.org/tags/web/&quot; class=&quot;entry-tag&quot;&gt;web&lt;/a&gt;, 
		&lt;a href=&quot;https://www.atomwolf.org/tags/computers/&quot; class=&quot;entry-tag&quot;&gt;computers&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Created&lt;/span&gt;: &lt;time datetime=&quot;2024-07-18&quot;&gt;18 July 2024&lt;/time&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;hr&gt;&lt;script type=&quot;module&quot; src=&quot;https://www.atomwolf.org/js/super-outline-effect.es.min.js&quot;&gt;&lt;/script&gt;
&lt;p&gt;Outlines are key to discerning details in 3D CAD models. Let’s compare the modeling view from Fusion 360 with a render from &lt;a href=&quot;https://modelviewer.dev/&quot;&gt;model-viewer&lt;/a&gt;:&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/images/comparing-fusion-360-and-model-viewer/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-256w.avif 256w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-410w.avif 410w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-512w.avif 512w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-650w.avif 650w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-850w.avif 850w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1075w.avif 1075w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1243w.avif 1243w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-256w.webp 256w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-410w.webp 410w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-512w.webp 512w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-650w.webp 650w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-850w.webp 850w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1075w.webp 1075w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1243w.webp 1243w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-256w.png 256w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-410w.png 410w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-512w.png 512w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-650w.png 650w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-850w.png 850w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1075w.png 1075w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1243w.png 1243w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A comparison of two views of the same plastic case with a window, internal standoffs, and connector holes. The top view is isometric, outlined, and isn&#39;t trying to be photorealistic. The bottom view has perspective, isn&#39;t outlined, and the materials look more complicated. The bottom view is so evenly lit that it&#39;s difficult to ascertain detail.&quot; loading=&quot;eager&quot; src=&quot;https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-256w.jpeg&quot; width=&quot;1243&quot; height=&quot;1938&quot; srcset=&quot;https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-256w.jpeg 256w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-410w.jpeg 410w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-512w.jpeg 512w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-650w.jpeg 650w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-850w.jpeg 850w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1075w.jpeg 1075w, https://www.atomwolf.org/img/comparing-fusion-360-and-model-viewer-hUS_QpJiqD-1243w.jpeg 1243w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Comparison of CAD modeling view (top) from Fusion 360 and a rendered view (bottom) from model-viewer&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The same model looks different, even ignoring the slight camera position difference due to manual positioning. The lighting and shadows are different, the materials look different, and the colors are different. The &lt;a href=&quot;https://en.wikipedia.org/wiki/3D_projection&quot;&gt;perspective&lt;/a&gt; is different. Even if I adjusted the lighting, added textures, and used realistic material settings, I’d prefer looking at details in the CAD view because of the outlines.&lt;label for=&quot;sn-previouslyon&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-previouslyon&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;I&#39;ve written about this elsewhere in this series, including &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/&quot;&gt;Experimenting with CAD models in model-viewer&lt;/a&gt;.&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;Could I add outlines? Maybe! I didn’t know anything about how model-viewer worked. I browsed the documentation around extensions and modifications, then set the project aside to leave an opening for inspiration.&lt;/p&gt;
&lt;h2 id=&quot;inspired-by-m-bius&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#inspired-by-m-bius&quot;&gt;Inspired by Mœbius&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A few weeks later, I ran into an &lt;a href=&quot;https://blog.maximeheckel.com/posts/moebius-style-post-processing/&quot;&gt;in-depth article&lt;/a&gt; about creating a post-processing effect inspired by the French artist &lt;a href=&quot;https://en.wikipedia.org/wiki/Jean_Giraud&quot;&gt;Mœbius&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/images/voyage-d-hermes/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-256w.avif 256w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-410w.avif 410w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-512w.avif 512w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-650w.avif 650w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-850w.avif 850w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1075w.avif 1075w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1280w.avif 1280w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1420w.avif 1420w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1660w.avif 1660w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1860w.avif 1860w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1882w.avif 1882w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-256w.webp 256w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-410w.webp 410w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-512w.webp 512w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-650w.webp 650w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-850w.webp 850w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1075w.webp 1075w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1280w.webp 1280w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1420w.webp 1420w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1660w.webp 1660w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1860w.webp 1860w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1882w.webp 1882w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;Illustration by Mœbius of a fantastical cityscape in the desert, drawn with fine black lines and even coloring. Large, rounded structures in soft purple hues fill the foreground, adorned with windows and balconies. A golden airship floats between the buildings. Cliffs rise in the background, and the ground is covered in rocks and scrub.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-256w.jpeg&quot; width=&quot;1882&quot; height=&quot;1440&quot; srcset=&quot;https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-256w.jpeg 256w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-410w.jpeg 410w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-512w.jpeg 512w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-650w.jpeg 650w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-850w.jpeg 850w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1075w.jpeg 1075w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1280w.jpeg 1280w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1420w.jpeg 1420w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1660w.jpeg 1660w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1860w.jpeg 1860w, https://www.atomwolf.org/img/voyagehermes-2-m%C5%93bius-rjf1BBI_zJ-1882w.jpeg 1882w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Plate 2 of Voyage d’Hermès, by Mœbius. Image from &lt;a href=&quot;https://www.sci-fi-o-rama.com/2020/05/15/voyage-dhermes-a-moebius-masterwork/&quot;&gt;Sci-Fi-O-Rama&lt;/a&gt;.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This article builds a shader, step-by-step, to add outlines, adjust shadows, and create a hand-drawn appearance to a rendered scene.&lt;/p&gt;
&lt;p&gt;To extend model-viewer, you can build on its foundation, &lt;a href=&quot;https://threejs.org/&quot;&gt;three.js&lt;/a&gt;, or use &lt;a href=&quot;https://modelviewer.dev/examples/postprocessing/&quot;&gt;model-viewer-effects&lt;/a&gt;, which adds post-processing effects. The article used &lt;a href=&quot;https://github.com/pmndrs/react-three-fiber&quot;&gt;react-three-fiber&lt;/a&gt;, a library that connects React to three.js! I couldn’t copy-and-paste the code samples—I couldn’t find an example for model-viewer-effects using a custom shader—but this was exactly the inspiration I needed.&lt;/p&gt;
&lt;h2 id=&quot;adding-a-custom-shader-to-model-viewer&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#adding-a-custom-shader-to-model-viewer&quot;&gt;Adding a custom shader to model-viewer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I couldn’t find a demo with a custom shader and model-viewer, so I needed to create one. Per the &lt;a href=&quot;https://modelviewer.dev/examples/postprocessing/&quot;&gt;documentation&lt;/a&gt;, I set up &lt;a href=&quot;https://github.com/pmndrs/postprocessing&quot;&gt;postprocessing&lt;/a&gt; and model-viewer-effects. model-viewer-effect’s documentation on &lt;a href=&quot;https://modelviewer.dev/examples/postprocessing/#custom-effects&quot;&gt;custom effects&lt;/a&gt; aims folks at the Postprocessing &lt;a href=&quot;https://github.com/pmndrs/postprocessing/wiki/Custom-Effects&quot;&gt;Custom Effects&lt;/a&gt; wiki page. After reading and re-reading the Custom Effects wiki page, I had a small understanding of how it works.&lt;/p&gt;
&lt;p&gt;A scene is rendered and given to the custom effects. The effect runs C-like code for each pixel of the source image, outputting a pixel used for the output image.&lt;/p&gt;
&lt;p&gt;With much effort, I had the skeleton of a custom effect rigged up to model-viewer. I was able to use it to add a partial outline to the model. This was a promising start, but I couldn’t make any improvements. I needed a deeper understanding.&lt;/p&gt;
&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#background&quot;&gt;Background&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL&lt;/a&gt; is a JavaScript API that uses &lt;a href=&quot;https://en.wikipedia.org/wiki/OpenGL_ES&quot;&gt;OpenGL ES&lt;/a&gt; for high-performance 2D and 3D graphics rendering in the browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://threejs.org/&quot;&gt;Three.js&lt;/a&gt; (&lt;a href=&quot;https://github.com/mrdoob/three.js/&quot;&gt;GitHub&lt;/a&gt;) is a JavaScript library that usually uses WebGL for rendering.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://modelviewer.dev/&quot;&gt;model-viewer&lt;/a&gt; (&lt;a href=&quot;https://github.com/google/model-viewer&quot;&gt;GitHub&lt;/a&gt;) is a JavaScript library and Web Component that uses three.js to display 3D models in the browser.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The Poimandres &lt;a href=&quot;https://github.com/pmndrs/postprocessing&quot;&gt;postprocessing&lt;/a&gt; library adds post-processing effects to three.js.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://modelviewer.dev/examples/postprocessing/&quot;&gt;model-viewer-effects&lt;/a&gt; (&lt;a href=&quot;https://github.com/google/model-viewer/tree/master/packages/model-viewer-effects&quot;&gt;GitHub&lt;/a&gt;) is a library and Web Component that uses the postprocessing library to add effects to model-viewer.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;what-is-post-processing&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#what-is-post-processing&quot;&gt;What is post-processing?&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Video_post-processing#Uses_in_3D_rendering&quot;&gt;Post-processing&lt;/a&gt; involves rendering a scene to an intermediate buffer before applying effects. Some effects can be applied directly to the intermediate render. Other effects are more complex, needing multiple passes or additional information.&lt;/p&gt;

&lt;h3 id=&quot;what-is-a-shader&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#what-is-a-shader&quot;&gt;What is a shader?&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;OpenGL renders images using a &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Rendering_Pipeline_Overview&quot;&gt;rendering pipeline&lt;/a&gt;.&lt;label for=&quot;sn-opengl&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-opengl&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;The Khronos Group will &lt;a href=&quot;https://www.khronos.org/opengl/wiki/FAQ#Where_can_I_download_OpenGL?&quot;&gt;remind you&lt;/a&gt; that an OpenGL &lt;i&gt;implementation&lt;/i&gt; renders images, while OpenGL is only &lt;a href=&quot;https://www.khronos.org/opengl/wiki/FAQ#What_is_OpenGL?&quot;&gt;a specification and an API definition&lt;/a&gt; for rendering.&lt;/small&gt;
Some pipeline steps run programs called &lt;a href=&quot;https://en.wikipedia.org/wiki/Shader&quot;&gt;shaders&lt;/a&gt;. There are different types of shaders, like vertex shaders, which run for each vertex of the geometry, and fragment shaders, which run for each &lt;a href=&quot;https://www.khronos.org/opengl/wiki/Fragment&quot;&gt;fragment&lt;/a&gt;, which is almost the same as a pixel. Shaders vary, but the prototypical vertex shader transforms a vertex’s 3D coordinates to a 2D screen position, and the prototypical fragment shader sets the color of a pixel. OpenGL shaders are typically written in OpenGL Shading Language (GLSL), similar to C.&lt;/p&gt;
&lt;h2 id=&quot;creating-the-outline-effect&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#creating-the-outline-effect&quot;&gt;Creating the outline effect&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are many ways to make outlines in 3D graphics.&lt;label for=&quot;sn-waystooutline&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-waystooutline&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;See &lt;a href=&quot;https://ameye.dev/notes/rendering-outlines/&quot;&gt;5 ways to draw an outline&lt;/a&gt; and &lt;a href=&quot;http://geoffprewett.com/blog/software/opengl-outline/&quot;&gt;Generating Beautiful 3D Outlines&lt;/a&gt;, for instance.&lt;/small&gt;
One method uses a fragment shader. Pixel-by-pixel, it looks for edges and draws outlines on the detected edges. It uses different versions of the scene to detect more edges.&lt;/p&gt;
&lt;p&gt;Based on advice from some of the articles I had found, I started with a grayscale version of the rendered scene.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;A low-contrast gray-on-gray rendering of a rectangular case, against a black background.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/luma-qas3qVzgXW-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/luma-qas3qVzgXW-256w.webp 256w, https://www.atomwolf.org/img/luma-qas3qVzgXW-410w.webp 410w, https://www.atomwolf.org/img/luma-qas3qVzgXW-512w.webp 512w, https://www.atomwolf.org/img/luma-qas3qVzgXW-650w.webp 650w, https://www.atomwolf.org/img/luma-qas3qVzgXW-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;luma&quot; outline-enabled=&quot;false&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;The grayscale version of the rendered scene&#39;s colors&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I use the &lt;a href=&quot;https://en.wikipedia.org/wiki/Sobel_operator&quot;&gt;Sobel operator&lt;/a&gt; to detect edges. For each point in the image, the Sobel operator looks at the eight neighboring points to determine if the center point is an edge.&lt;label for=&quot;sn-kernel&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-kernel&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;Many image operations can be done by looking at a small window of pixels around each pixel. Each output pixel is created through some math done on that small window of input pixels. This is called &lt;a href=&quot;https://en.wikipedia.org/wiki/Kernel_(image_processing)&quot;&gt;convolution&lt;/a&gt;. It can be tricky to understand. The Mœbius shader article has &lt;a href=&quot;https://blog.maximeheckel.com/posts/moebius-style-post-processing/#edge-detection-with-sobel-filters&quot;&gt;an interactive Sobel demo&lt;/a&gt;, and I found a similar &lt;a href=&quot;https://setosa.io/ev/image-kernels/&quot;&gt;interactive sharpening demo&lt;/a&gt;. If you know of something better, please let me know!&lt;/small&gt;
The Sobel operator isn’t perfect, but it’s easy to implement in a fragment shader. If the Sobel operator’s calculated value is higher than some threshold, I count it as an edge and color the pixel black.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;Black edges on a white background, outlining a rectangular case. The outline is somewhat complete.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/luma-edges-zDqBpKKN32-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/luma-edges-zDqBpKKN32-256w.webp 256w, https://www.atomwolf.org/img/luma-edges-zDqBpKKN32-410w.webp 410w, https://www.atomwolf.org/img/luma-edges-zDqBpKKN32-512w.webp 512w, https://www.atomwolf.org/img/luma-edges-zDqBpKKN32-650w.webp 650w, https://www.atomwolf.org/img/luma-edges-zDqBpKKN32-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;none&quot; outline-enabled=&quot;true&quot; depth-coefficient=&quot;0&quot; normal-coefficient=&quot;0&quot; luma-coefficient=&quot;2.5&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;Edges detected from the grayscale version of the rendered scene&#39;s colors&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It does an okay job—better than I was expecting! Much of the interior geometry is lost, but the silhouette and some details are outlined.&lt;/p&gt;
&lt;p&gt;The next aspect used to add edges is depth.  The depth map is a grayscale image where the color represents the distance from the viewpoint to the object.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;A hard to see gray-on-gray rendering of a rectangular case, against a gray background. It is difficult to make out any details.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/depth-RQlQK_DxS7-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/depth-RQlQK_DxS7-256w.webp 256w, https://www.atomwolf.org/img/depth-RQlQK_DxS7-410w.webp 410w, https://www.atomwolf.org/img/depth-RQlQK_DxS7-512w.webp 512w, https://www.atomwolf.org/img/depth-RQlQK_DxS7-650w.webp 650w, https://www.atomwolf.org/img/depth-RQlQK_DxS7-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;depth&quot; outline-enabled=&quot;false&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;The depth map, where the pixel brightness represents how far away the point is from the viewpoint&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Once again, the Sobel operator detects edges.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;Black edges on a white background, outlining a rectangular case. The outer outline is complete, but the inner details are sparse.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/depth-edges-pJwgsbI6Nu-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/depth-edges-pJwgsbI6Nu-256w.webp 256w, https://www.atomwolf.org/img/depth-edges-pJwgsbI6Nu-410w.webp 410w, https://www.atomwolf.org/img/depth-edges-pJwgsbI6Nu-512w.webp 512w, https://www.atomwolf.org/img/depth-edges-pJwgsbI6Nu-650w.webp 650w, https://www.atomwolf.org/img/depth-edges-pJwgsbI6Nu-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;none&quot; outline-enabled=&quot;true&quot; depth-coefficient=&quot;10&quot; normal-coefficient=&quot;0&quot; luma-coefficient=&quot;0&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;Edges detected from the depth map&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Using the Sobel operator to detect edges using the depth map reveals edges around the window, the cutouts, and the silhouette. Most other edges are missed.&lt;/p&gt;
&lt;p&gt;The next aspect used to detect edges is the normal map. The &lt;a href=&quot;https://en.wikipedia.org/wiki/Normal_(geometry)&quot;&gt;normal&lt;/a&gt; of a surface is the direction the surface faces, and a &lt;a href=&quot;https://en.wikipedia.org/wiki/Normal_mapping&quot;&gt;normal map&lt;/a&gt; is an image that colors each surface based on the direction the surface faces.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;A rendering of a rectangular case, in a muted pinks, blues, and greens, against a blue background. The colors are super cool.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/normal-XxiOaiyi8Y-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/normal-XxiOaiyi8Y-256w.webp 256w, https://www.atomwolf.org/img/normal-XxiOaiyi8Y-410w.webp 410w, https://www.atomwolf.org/img/normal-XxiOaiyi8Y-512w.webp 512w, https://www.atomwolf.org/img/normal-XxiOaiyi8Y-650w.webp 650w, https://www.atomwolf.org/img/normal-XxiOaiyi8Y-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;normals&quot; outline-enabled=&quot;false&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;The normal map, where colors represent the direction the surface faces&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Using the Sobel operator, again, we highlight detected edges in the normal map.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;Black edges on a white background, outlining a rectangular case. The outline is mostly complete.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/normal-edges-p5B4nUvZ_w-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/normal-edges-p5B4nUvZ_w-256w.webp 256w, https://www.atomwolf.org/img/normal-edges-p5B4nUvZ_w-410w.webp 410w, https://www.atomwolf.org/img/normal-edges-p5B4nUvZ_w-512w.webp 512w, https://www.atomwolf.org/img/normal-edges-p5B4nUvZ_w-650w.webp 650w, https://www.atomwolf.org/img/normal-edges-p5B4nUvZ_w-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;none&quot; outline-enabled=&quot;true&quot; depth-coefficient=&quot;0&quot; normal-coefficient=&quot;10&quot; luma-coefficient=&quot;0&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;Edges detected from the normal map&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Wow! For this model, at least, the detected edges create a detailed outline. Parallel surfaces without intermediate geometry seem to be the cause of most missing edges.&lt;label for=&quot;sn-surfaceids&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-surfaceids&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;There is at least one clever improvement to this method documented in the &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#resources&quot;&gt;resources&lt;/a&gt;.&lt;/small&gt;
For example, when looking at the top from an angle, the far edges of the buttons usually aren’t outlined.&lt;/p&gt;
&lt;p&gt;Next, I combine all the detected edges.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;Black edges on a white background, outlining a rectangular case. The outline is quite good.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/combined-edges-lO_KaEUnMA-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/combined-edges-lO_KaEUnMA-256w.webp 256w, https://www.atomwolf.org/img/combined-edges-lO_KaEUnMA-410w.webp 410w, https://www.atomwolf.org/img/combined-edges-lO_KaEUnMA-512w.webp 512w, https://www.atomwolf.org/img/combined-edges-lO_KaEUnMA-650w.webp 650w, https://www.atomwolf.org/img/combined-edges-lO_KaEUnMA-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer&gt;
          &lt;super-outline-effect model-colors=&quot;none&quot; outline-enabled=&quot;true&quot; depth-coefficient=&quot;10&quot; normal-coefficient=&quot;10&quot; luma-coefficient=&quot;2.5&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;Edges detected from the grayscale version of the scene&#39;s colors, the normal map, or the depth map&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This looks pretty good!&lt;/p&gt;
&lt;p&gt;Let’s overlay the edges on the rendered scene.&lt;/p&gt;
&lt;figure&gt;
      &lt;model-viewer src=&quot;/files/adding-outlines-in-post-processing/usdz.glb&quot; camera-orbit=&quot;47.47deg 49.8deg 0.2154m&quot; field-of-view=&quot;30deg&quot; camera-controls=&quot;&quot;&gt;
&lt;img alt=&quot;A rectangular case in two halves, with a window cutout on top. The top half is pink, and the bottom half is yellow. The geometry is outlined in thin black lines.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/combined-cS4CwFZQ-Z-256w.webp&quot; width=&quot;850&quot; height=&quot;850&quot; srcset=&quot;https://www.atomwolf.org/img/combined-cS4CwFZQ-Z-256w.webp 256w, https://www.atomwolf.org/img/combined-cS4CwFZQ-Z-410w.webp 410w, https://www.atomwolf.org/img/combined-cS4CwFZQ-Z-512w.webp 512w, https://www.atomwolf.org/img/combined-cS4CwFZQ-Z-650w.webp 650w, https://www.atomwolf.org/img/combined-cS4CwFZQ-Z-850w.webp 850w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot; slot=&quot;poster&quot;&gt;
        &lt;effect-composer rendermode=&quot;quality&quot;&gt;
          &lt;super-outline-effect model-colors=&quot;material&quot; outline-enabled=&quot;true&quot; outline-thickness=&quot;1&quot; depth-coefficient=&quot;10&quot; normal-coefficient=&quot;10&quot; luma-coefficient=&quot;2.5&quot; outline-color=&quot;black&quot; shaky-enabled=&quot;false&quot;&gt;&lt;/super-outline-effect&gt;
          &lt;color-grade-effect&gt;&lt;/color-grade-effect&gt;
        &lt;/effect-composer&gt;
      &lt;/model-viewer&gt;
&lt;figcaption&gt;
&lt;p&gt;Detected edges overlaid upon the original rendered scene&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Looking critically, the A and B labels are missing outlines from most viewpoints. Other edges are missing, and there are often so many outlines drawn on the standoffs that it looks like shading. There’s an issue with aliasing.&lt;/p&gt;
&lt;p&gt;It isn’t perfect, but this looks even better than I had hoped!&lt;/p&gt;
&lt;h2 id=&quot;demos&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#demos&quot;&gt;Demos&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While working on this article, I made a tool to take screenshots of the shader in various configurations. I kajiggered it into &lt;a href=&quot;https://outline-experiments-adamwolf.replit.app/playground/&quot;&gt;an interactive demo where you can play with the shader settings&lt;/a&gt;. As I was brainstorming ways to present an explanation, I made another demo that shows &lt;a href=&quot;https://outline-experiments-adamwolf.replit.app/mirror/&quot;&gt;all the views of the model&lt;/a&gt; in synchrony.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#next-steps&quot;&gt;Next Steps&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;shader-improvements&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#shader-improvements&quot;&gt;Shader Improvements&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Assuming this works as well on other models, it’s good enough for me to consider outlines handled. In case I change my mind (or for the inevitable tinkering), I’ve collected some articles that detail improvements and alternate approaches.&lt;/p&gt;
&lt;p&gt;I haven’t yet reviewed the shader with care. I built this shader through experimentation and chose the various settings haphazardly. I’m certain there are real improvements to be made and bugs to fix, even before improving the actual algorithm.&lt;/p&gt;
&lt;h3 id=&quot;moving-on&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#moving-on&quot;&gt;Moving on&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The next step is to wrap this outline effect into a Web Component that integrates with model-viewer-effects. No one should have to write JavaScript to add this effect. It’ll go inside &lt;code&gt;&amp;lt;model-viewer&amp;gt;&lt;/code&gt; tags like the built-in effects.&lt;/p&gt;
&lt;p&gt;Additionally, I want to create a one-page “quick and dirty” example for using a custom shader with model-viewer, and an example model-viewer-effect-alike Web Component. I don’t think custom shaders for model-viewer are commonly asked about, but I want to help document them.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/#resources&quot;&gt;Resources&lt;/a&gt;&lt;/h2&gt;
 
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API&quot;&gt;WebGL: 2D and 3D graphics for the web&lt;/a&gt; Mozilla’s WebGL documentation. It includes reference material, tutorials, guides, articles, and talks. It seems to be a good starting point, and what I’ve read has been clear and helpful.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.khronos.org/opengl/wiki/&quot;&gt;Khronos OpenGL wiki&lt;/a&gt; Khronos, the group behind OpenGL, has an in-depth wiki.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ameye.dev/notes/rendering-outlines/&quot;&gt;5 ways to draw an outline&lt;/a&gt; Overview of five ways to draw outlines in 3D graphics.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://geoffprewett.com/blog/software/opengl-outline/&quot;&gt;Generating Beautiful 3D Outlines&lt;/a&gt; Three ways to draw outlines: offset and scale, stencil and wireframe, and post-processing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.maximeheckel.com/posts/moebius-style-post-processing/&quot;&gt;Moebius-style post-processing and other stylized shaders&lt;/a&gt; Quite good. Dissects “Mœbius style”, which includes outlines. Uses post-processing with depth and normals. Code is JavaScript, using react-three-fiber, but definitely doesn’t require React knowledge to understand.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tympanus.net/codrops/2022/11/29/sketchy-pencil-effect-with-three-js-post-processing/&quot;&gt;Sketchy Pencil Effect with Three.js Post-Processing&lt;/a&gt; Uses post-processing with normals and grayscale version of the rendered scene. Code is JavaScript, using three.js.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://ameye.dev/notes/edge-detection-outlines/&quot;&gt;Edge Detection Outlines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://omar-shehata.medium.com/how-to-render-outlines-in-webgl-8253c14724f9&quot;&gt;How to render outlines in WebGL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.ronja-tutorials.com/post/019-postprocessing-outlines/&quot;&gt;Outlines via Postprocessing&lt;br&gt;
&lt;/a&gt; Post-processing, uses depth and normals. Code is C# for Unity.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://roystan.net/articles/outline-shader/&quot;&gt;Unity Outline Shader&lt;/a&gt; A detailed walkthrough creating and improving an outline shader in Unity. Code is C#. It seems to be similar but significantly improved from what I’m doing here.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://omar-shehata.medium.com/better-outline-rendering-using-surface-ids-with-webgl-e13cdab1fd94&quot;&gt;Better outline rendering using surface IDs with WebGL&lt;/a&gt; (&lt;a href=&quot;https://github.com/OmarShehata/webgl-outlines/&quot;&gt;GitHub&lt;/a&gt;) Highlights issues with using normals, and assigns surface IDs instead.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.videopoetics.com/tutorials/pixel-perfect-outline-shaders-unity/&quot;&gt;Pixel-Perfect Outline Shaders for Unity&lt;/a&gt; Different method than our post-processing edge-detection, but good article. Uses a vertex shader to add a slightly larger mesh. Code is for Unity.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jlKNOirh66E&quot;&gt;Moebius-style 3D Rendering&lt;/a&gt; ~8-minute video. Breaks down the “Mœbius style”, and imitates it in Unity. Helpful explanation and visuals. Beginner-friendly, but it expects 3D rendering knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;hr&gt;&lt;p&gt;&lt;a href=&quot;mailto:feedcomments@atomwolf.org?subject=Project%20Feed%20Reply%20for%20Rendering%20Outlines%20with%20a%20Post-processing%20Shader&amp;amp;body=(From%20project%20feed%20entry%3A%20https%3A%2F%2Fwww.atomwolf.org%2Fposts%2Frendering-outlines-with-a-post-processing-shader%2F)&quot;&gt;Reply via email&lt;/a&gt;&lt;/p&gt;
		</content>
	</entry>
	<entry>
		<title>Experimenting with CAD models in model-viewer</title>
		<link href="https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/"/>
		<updated>2024-06-10T00:00:00Z</updated>
		<id>https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/</id>
		 <content type="html">
&lt;div class=&quot;dotted-metadata-block&quot;&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Summary&lt;/span&gt;: I use Blender to export models from Fusion 360 to model-viewer, and I investigate unexpected visual problems.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Assumed audience&lt;/span&gt;: folks on the web&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Project&lt;/span&gt;: part of &lt;a href=&quot;https://www.atomwolf.org/projects/showcasing-3d-cad-models/&quot;&gt;Showcasing 3D CAD Models on the Web&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;span class=&quot;small-caps&quot;&gt;Tags&lt;/span&gt;:
		&lt;a href=&quot;https://www.atomwolf.org/tags/web/&quot; class=&quot;entry-tag&quot;&gt;web&lt;/a&gt;, 
		&lt;a href=&quot;https://www.atomwolf.org/tags/computers/&quot; class=&quot;entry-tag&quot;&gt;computers&lt;/a&gt;, 
		&lt;a href=&quot;https://www.atomwolf.org/tags/3d-graphics/&quot; class=&quot;entry-tag&quot;&gt;3D graphics&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Created&lt;/span&gt;: &lt;time datetime=&quot;2024-06-10&quot;&gt;10 June 2024&lt;/time&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;hr&gt;&lt;p&gt;After exploring options for showcasing 3D CAD models on the web and compiling a list of desired features, I’m ready to experiment with Google’s model-viewer, using a real model I’ve been working on.&lt;/p&gt;
&lt;p&gt;I’m making a small case for a &lt;a href=&quot;https://www.lilygo.cc/products/t-display-s3&quot;&gt;T-Display S3&lt;/a&gt; with a few buttons, a cutout for the screen and the USB-C port, a small prototyping PCB, and two Molex SL connectors. I exported some images from the modeling view in Fusion 360.&lt;label for=&quot;sn-fusion360&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-fusion360&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Fusion_360&quot;&gt;Fusion 360&lt;/a&gt;, a closed-source CAD suite, is my go-to for engineering 3D models. Although Autodesk recently added some features I&#39;ve been wanting for years, like &lt;a href=&quot;https://help.autodesk.com/view/fusion360/ENU/?guid=CFG-OVERVIEW&quot;&gt;Configurations&lt;/a&gt;, I worry about its future.  Wikipedia informs me it&#39;s been recently rebranded as just &quot;Fusion&quot;.&lt;/small&gt;
&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/cad-view-with-electronics/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-256w.avif 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-410w.avif 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-512w.avif 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-650w.avif 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-850w.avif 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1075w.avif 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1280w.avif 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-256w.webp 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-410w.webp 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-512w.webp 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-650w.webp 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-850w.webp 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1075w.webp 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1280w.webp 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-256w.png 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-410w.png 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-512w.png 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-650w.png 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-850w.png 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1075w.png 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1280w.png 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A cropped screenshot from Fusion 360 showing the plastic case, connectors, and the display of the T-Display S3&quot; loading=&quot;eager&quot; src=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-256w.jpeg&quot; width=&quot;1280&quot; height=&quot;1024&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-256w.jpeg 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-410w.jpeg 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-512w.jpeg 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-650w.jpeg 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-850w.jpeg 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1075w.jpeg 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-full-fUThdBuhnG-1280w.jpeg 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Showing all the components in Fusion 360’s modeling view&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To show the case details, I hid the electronics and the connectors.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/plastics-cad-view/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-256w.avif 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-410w.avif 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-512w.avif 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-650w.avif 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-850w.avif 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1075w.avif 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1280w.avif 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-256w.webp 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-410w.webp 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-512w.webp 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-650w.webp 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-850w.webp 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1075w.webp 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1280w.webp 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-256w.png 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-410w.png 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-512w.png 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-650w.png 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-850w.png 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1075w.png 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1280w.png 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A cropped screenshot from Fusion 360 showing the plastic case without the electronics or connectors. The cutout for the display reveals internal standoffs.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-256w.jpeg&quot; width=&quot;1280&quot; height=&quot;1024&quot; srcset=&quot;https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-256w.jpeg 256w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-410w.jpeg 410w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-512w.jpeg 512w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-650w.jpeg 650w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-850w.jpeg 850w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1075w.jpeg 1075w, https://www.atomwolf.org/img/Case-(T-Display-S3)-v7-plastics-only-KBDJYaqVnx-1280w.jpeg 1280w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Showing only the printed components in Fusion 360’s modeling view&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;exporting-from-fusion-360&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#exporting-from-fusion-360&quot;&gt;Exporting from Fusion 360&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;model-viewer requires models in glTF format (or GLB, glTF’s binary equivalent). Fusion 360 doesn’t export to these formats without a plugin.&lt;label for=&quot;sn-plugin&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-plugin&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;I found two proprietary glTF exporters, one by &lt;a href=&quot;https://prototechsolutions.com/3d-products/fusion-360/gltf-exporter/&quot;&gt;ProtoTech Solutions&lt;/a&gt; and another by &lt;a href=&quot;https://www.simlab-soft.com/3d-plugins/Fusion360/GLTF_Exporter_For_Fusion-main.html&quot;&gt;SimLab&lt;/a&gt;, but I want to avoid a closed-source plugin.&lt;/small&gt;
Based on the &lt;a href=&quot;https://modelviewer.dev/docs/faq.html#entrydocs-tools-questions-export&quot;&gt;model-viewer FAQ’s recommendation&lt;/a&gt;, I decided to try &lt;a href=&quot;https://blender.org&quot;&gt;Blender&lt;/a&gt; as a converter.&lt;label for=&quot;sn-blender&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-blender&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;I like Blender. &lt;a href=&quot;https://www.blender.org/&quot;&gt;Blender&lt;/a&gt; is open source, and it&#39;s used by professionals and hobbyists. I haven&#39;t spent more than an hour or two with it. though, which isn&#39;t even enough time to make &lt;a href=&quot;https://www.blenderguru.com/&quot;&gt;a good donut&lt;/a&gt;.&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;I exported the model from Fusion 360 in both FBX and USDz formats for comparison.&lt;/p&gt;
&lt;h2 id=&quot;fbx&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#fbx&quot;&gt;FBX&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/FBX&quot;&gt;FBX&lt;/a&gt; is a proprietary file format controlled by Autodesk.&lt;/p&gt;
&lt;p&gt;When importing the FBX into Blender using defaults, the model was oddly oriented.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/blender-fbx-import/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-256w.avif 256w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-410w.avif 410w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-512w.avif 512w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-650w.avif 650w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-850w.avif 850w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1075w.avif 1075w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1280w.avif 1280w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1420w.avif 1420w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1660w.avif 1660w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1860w.avif 1860w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-2048w.avif 2048w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-3134w.avif 3134w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-256w.webp 256w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-410w.webp 410w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-512w.webp 512w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-650w.webp 650w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-850w.webp 850w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1075w.webp 1075w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1280w.webp 1280w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1420w.webp 1420w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1660w.webp 1660w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1860w.webp 1860w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-2048w.webp 2048w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-3134w.webp 3134w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-256w.png 256w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-410w.png 410w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-512w.png 512w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-650w.png 650w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-850w.png 850w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1075w.png 1075w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1280w.png 1280w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1420w.png 1420w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1660w.png 1660w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1860w.png 1860w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-2048w.png 2048w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-3134w.png 3134w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A screenshot of Blender showing the plastic case on its end.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-256w.jpeg&quot; width=&quot;3134&quot; height=&quot;2112&quot; srcset=&quot;https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-256w.jpeg 256w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-410w.jpeg 410w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-512w.jpeg 512w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-650w.jpeg 650w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-850w.jpeg 850w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1075w.jpeg 1075w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1280w.jpeg 1280w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1420w.jpeg 1420w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1660w.jpeg 1660w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-1860w.jpeg 1860w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-2048w.jpeg 2048w, https://www.atomwolf.org/img/blender-fbx-import-6PsqPryEeX-3134w.jpeg 3134w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;FBX model imported into Blender&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After some tinkering, I found that setting the import options for Manual Orientation to “Y Forward” and “Z Up” in Blender’s FBX import dialog resolved the orientation issues. Exporting to glTF 2.0 with the “+Y Up” option checked produced a correctly oriented model in model-viewer.&lt;/p&gt;
&lt;p&gt;To check scaling, I used the validation report in model-viewer’s editor. The model dimensions matched the dimensions in Fusion 360.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/fbx-export-details/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-256w.avif 256w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-410w.avif 410w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-512w.avif 512w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-650w.avif 650w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-850w.avif 850w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1075w.avif 1075w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1280w.avif 1280w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1420w.avif 1420w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1660w.avif 1660w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1860w.avif 1860w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2048w.avif 2048w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2862w.avif 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-256w.webp 256w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-410w.webp 410w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-512w.webp 512w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-650w.webp 650w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-850w.webp 850w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1075w.webp 1075w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1280w.webp 1280w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1420w.webp 1420w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1660w.webp 1660w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1860w.webp 1860w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2048w.webp 2048w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2862w.webp 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-256w.png 256w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-410w.png 410w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-512w.png 512w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-650w.png 650w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-850w.png 850w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1075w.png 1075w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1280w.png 1280w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1420w.png 1420w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1660w.png 1660w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1860w.png 1860w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2048w.png 2048w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2862w.png 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A screenshot of model-viewer&#39;s editor showing the plastic case from the side with similar colors as the CAD view. The right side of the editor shows controls and sample HTML.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-256w.jpeg&quot; width=&quot;2862&quot; height=&quot;1662&quot; srcset=&quot;https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-256w.jpeg 256w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-410w.jpeg 410w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-512w.jpeg 512w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-650w.jpeg 650w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-850w.jpeg 850w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1075w.jpeg 1075w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1280w.jpeg 1280w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1420w.jpeg 1420w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1660w.jpeg 1660w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-1860w.jpeg 1860w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2048w.jpeg 2048w, https://www.atomwolf.org/img/fbx-editor-uCOv9ztGzy-2862w.jpeg 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Fusion 360 to FBX to glTF 2.0 via Blender, viewed in model-viewer’s editor&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With the Manual Orientation adjustment on importing FBX and the +Y Up option set when exporting to glTF 2.0, the glTF file has the correct orientation and scale.&lt;/p&gt;
&lt;h2 id=&quot;usdz&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#usdz&quot;&gt;USDz&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;USDz is an open-source format developed by Pixar, with wide support in Apple products. It’s part of &lt;a href=&quot;https://en.wikipedia.org/wiki/Universal_Scene_Description&quot;&gt;Universal Scene Description&lt;/a&gt; (USD).&lt;/p&gt;
&lt;p&gt;Importing the USDz file into Blender maintained the correct orientation, but I encountered scaling issues. Setting the import scale to 0.001 in Blender’s import dialog resolved this problem.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/usdz-export-details/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-256w.avif 256w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-410w.avif 410w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-512w.avif 512w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-650w.avif 650w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-850w.avif 850w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1075w.avif 1075w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1280w.avif 1280w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1420w.avif 1420w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1660w.avif 1660w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1860w.avif 1860w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2048w.avif 2048w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2858w.avif 2858w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-256w.webp 256w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-410w.webp 410w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-512w.webp 512w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-650w.webp 650w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-850w.webp 850w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1075w.webp 1075w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1280w.webp 1280w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1420w.webp 1420w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1660w.webp 1660w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1860w.webp 1860w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2048w.webp 2048w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2858w.webp 2858w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-256w.png 256w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-410w.png 410w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-512w.png 512w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-650w.png 650w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-850w.png 850w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1075w.png 1075w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1280w.png 1280w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1420w.png 1420w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1660w.png 1660w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1860w.png 1860w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2048w.png 2048w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2858w.png 2858w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A screenshot of model-viewer&#39;s editor showing the plastic case from the side with similar colors as the CAD view, but slightly different than the version exported through FBX. The right side of the editor shows controls and sample HTML.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-256w.jpeg&quot; width=&quot;2858&quot; height=&quot;1660&quot; srcset=&quot;https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-256w.jpeg 256w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-410w.jpeg 410w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-512w.jpeg 512w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-650w.jpeg 650w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-850w.jpeg 850w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1075w.jpeg 1075w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1280w.jpeg 1280w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1420w.jpeg 1420w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1660w.jpeg 1660w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-1860w.jpeg 1860w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2048w.jpeg 2048w, https://www.atomwolf.org/img/usdz-editor-iszvoR9bWQ-2858w.jpeg 2858w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Fusion 360 to USDz to glTF 2.0 via Blender, viewed in model-viewer’s editor&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;With the scale adjustment on importing USDz and the +Y Up option set when exporting to glTF 2.0, the glTF file has the correct orientation and scale.&lt;/p&gt;
&lt;h2 id=&quot;rendering-challenges&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#rendering-challenges&quot;&gt;Rendering Challenges&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;With the model successfully imported into model-viewer, I quickly realized that, compared to Fusion 360’s modeling view, it was difficult to see details in the model in model-viewer.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/neutral-lighting/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-256w.avif 256w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-410w.avif 410w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-512w.avif 512w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-650w.avif 650w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-850w.avif 850w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1075w.avif 1075w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1280w.avif 1280w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1420w.avif 1420w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1660w.avif 1660w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1860w.avif 1860w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2048w.avif 2048w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2864w.avif 2864w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-256w.webp 256w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-410w.webp 410w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-512w.webp 512w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-650w.webp 650w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-850w.webp 850w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1075w.webp 1075w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1280w.webp 1280w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1420w.webp 1420w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1660w.webp 1660w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1860w.webp 1860w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2048w.webp 2048w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2864w.webp 2864w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-256w.png 256w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-410w.png 410w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-512w.png 512w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-650w.png 650w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-850w.png 850w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1075w.png 1075w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1280w.png 1280w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1420w.png 1420w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1660w.png 1660w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1860w.png 1860w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2048w.png 2048w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2864w.png 2864w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A screenshot of model-viewer&#39;s editor showing the plastic case from above, with uniform coloring and few shadows.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-256w.jpeg&quot; width=&quot;2864&quot; height=&quot;1664&quot; srcset=&quot;https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-256w.jpeg 256w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-410w.jpeg 410w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-512w.jpeg 512w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-650w.jpeg 650w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-850w.jpeg 850w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1075w.jpeg 1075w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1280w.jpeg 1280w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1420w.jpeg 1420w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1660w.jpeg 1660w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-1860w.jpeg 1860w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2048w.jpeg 2048w, https://www.atomwolf.org/img/neutral-env-JcfXwP-ILc-2864w.jpeg 2864w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;model-viewer rendering with “neutral” lighting environment&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;model-viewer lets you control the lighting and environment around the model. The previous image showed the model using the default scene with even lighting on all sides.&lt;/p&gt;
&lt;p&gt;model-viewer also has a “legacy” environment meant for frontward viewing.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/legacy-lighting/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-256w.avif 256w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-410w.avif 410w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-512w.avif 512w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-650w.avif 650w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-850w.avif 850w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1075w.avif 1075w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1280w.avif 1280w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1420w.avif 1420w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1660w.avif 1660w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1860w.avif 1860w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2048w.avif 2048w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2862w.avif 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-256w.webp 256w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-410w.webp 410w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-512w.webp 512w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-650w.webp 650w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-850w.webp 850w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1075w.webp 1075w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1280w.webp 1280w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1420w.webp 1420w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1660w.webp 1660w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1860w.webp 1860w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2048w.webp 2048w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2862w.webp 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-256w.png 256w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-410w.png 410w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-512w.png 512w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-650w.png 650w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-850w.png 850w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1075w.png 1075w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1280w.png 1280w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1420w.png 1420w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1660w.png 1660w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1860w.png 1860w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2048w.png 2048w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2862w.png 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A screenshot of model-viewer&#39;s editor showing the plastic case from above. The model is well lit, but not uniformly lit, and the front right face is slightly shadowed.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-256w.jpeg&quot; width=&quot;2862&quot; height=&quot;1664&quot; srcset=&quot;https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-256w.jpeg 256w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-410w.jpeg 410w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-512w.jpeg 512w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-650w.jpeg 650w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-850w.jpeg 850w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1075w.jpeg 1075w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1280w.jpeg 1280w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1420w.jpeg 1420w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1660w.jpeg 1660w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-1860w.jpeg 1860w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2048w.jpeg 2048w, https://www.atomwolf.org/img/legacy-env-2qAhWZq-DJ-2862w.jpeg 2862w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;model-viewer rendering with “legacy” lighting environment&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The “legacy” environment reveals more details than the “neutral” environment. Neither option came close to the clarity of Fusion 360’s modeling view with its outline rendering.&lt;/p&gt;
&lt;h2 id=&quot;understanding-the-rendering-differences&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#understanding-the-rendering-differences&quot;&gt;Understanding the Rendering Differences&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This isn’t a case of model-viewer or glTF being “bad”. model-viewer &lt;a href=&quot;https://github.khronos.org/glTF-Render-Fidelity/comparison/&quot;&gt;does a great job rendering glTF&lt;/a&gt;.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/images/damaged-helmet/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-256w.avif 256w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-410w.avif 410w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-512w.avif 512w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-650w.avif 650w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-850w.avif 850w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1075w.avif 1075w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1280w.avif 1280w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1420w.avif 1420w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1660w.avif 1660w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1860w.avif 1860w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2048w.avif 2048w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2898w.avif 2898w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-256w.webp 256w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-410w.webp 410w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-512w.webp 512w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-650w.webp 650w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-850w.webp 850w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1075w.webp 1075w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1280w.webp 1280w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1420w.webp 1420w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1660w.webp 1660w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1860w.webp 1860w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2048w.webp 2048w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2898w.webp 2898w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-256w.png 256w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-410w.png 410w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-512w.png 512w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-650w.png 650w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-850w.png 850w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1075w.png 1075w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1280w.png 1280w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1420w.png 1420w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1660w.png 1660w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1860w.png 1860w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2048w.png 2048w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2898w.png 2898w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;model-viewer rendering a detailed, futuristic helmet. It looks realistic.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-256w.jpeg&quot; width=&quot;2898&quot; height=&quot;1702&quot; srcset=&quot;https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-256w.jpeg 256w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-410w.jpeg 410w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-512w.jpeg 512w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-650w.jpeg 650w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-850w.jpeg 850w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1075w.jpeg 1075w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1280w.jpeg 1280w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1420w.jpeg 1420w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1660w.jpeg 1660w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-1860w.jpeg 1860w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2048w.jpeg 2048w, https://www.atomwolf.org/img/damaged-helmet-GcL8iIsTrV-2898w.jpeg 2898w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;model-viewer rendering a damaged helmet, one of the example models in &lt;a href=&quot;https://modelviewer.dev/editor/&quot;&gt;model-viewer’s editor&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;glTF aims for photorealistic results through “&lt;a href=&quot;https://en.wikipedia.org/wiki/Physically_based_rendering&quot;&gt;physically based rendering&lt;/a&gt;”.&lt;label for=&quot;sn-pbr&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-pbr&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;glTF&#39;s physically based rendering (PBR) uses &quot;Bidirectional Scattering Distribution Functions&quot; and microfacets to model light interactions with surfaces. glTF includes a consistent set of parameters, helping to standardize PBR across various rendering engines. The Khronos Group, the consortium behind glTF, has &lt;a href=&quot;https://github.khronos.org/glTF-Tutorials/PBR/&quot;&gt;a detailed explanation of PBR in glTF&lt;/a&gt;.&lt;/small&gt;
&lt;/p&gt;
&lt;p&gt;Good results from physically based rendering need good parameters and textures for the materials. In Fusion 360, I don’t usually do much with materials, textures, or colors. For this model, I set the material to “Plastic” and set the colors to some defaults. The materials were exported through an intermediate format into glTF 2.0.&lt;label for=&quot;sn-color&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-color&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;Materials in physically based rendering have a base color and some surface parameters, but the rendered color of the surface may be quite different from the base color. Just like in the real world, the color of the light that bounces off a material isn&#39;t just the color of the material. It&#39;s influenced by the surface details and what the light was like before it interacted with the material. There are interactive lighting and color examples in the &lt;a href=&quot;https://modelviewer.dev/examples/lightingandenv/&quot;&gt;Lighting &amp;amp; Skybox&lt;/a&gt; model-viewer examples section.  model-viewer&#39;s &quot;neutral&quot; lighting environment was engineered to color materials closely to the material&#39;s base colors. For more, read &quot;&lt;a href=&quot;https://modelviewer.dev/examples/color.html&quot;&gt;Achieving Color-Accurate Presentation with glTF&lt;/a&gt;&quot; and &quot;&lt;a href=&quot;https://modelviewer.dev/examples/tone-mapping&quot;&gt;Tone Mapping Considerations for Physically-Based Rendering&lt;/a&gt;&quot;.&lt;/small&gt;
Even if I do well with the materials (and lighting), though, I should expect the render to show detail like a photo. I think the Fusion 360 modeling view does better than a photo.&lt;/p&gt;
&lt;h2 id=&quot;exploring-solutions&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#exploring-solutions&quot;&gt;Exploring solutions&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Can I turn on outlines like in Fusion 360’s modeling view? model-viewer supports &lt;a href=&quot;https://modelviewer.dev/examples/postprocessing/&quot;&gt;model-viewer-effects&lt;/a&gt;, an extension that changes the visuals. model-viewer-effect has an &lt;a href=&quot;https://modelviewer.dev/docs/mve#outline-attributes&quot;&gt;outline effect&lt;/a&gt;, but it only adds an outer outline around objects, not internal edges.&lt;/p&gt;
&lt;p&gt;Don’t lose hope! All of this is open source. model-viewer-effects uses &lt;a href=&quot;https://github.com/pmndrs/postprocessing&quot;&gt;postprocessing&lt;/a&gt;, a library that makes effects using vertex and fragment shaders. If I can’t use postprocessing, I could &lt;a href=&quot;https://modelviewer.dev/docs/faq.html#entrydocs-general-questions-three&quot;&gt;plumb through model-viewer into three.js&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;next-steps&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/#next-steps&quot;&gt;Next steps&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Can I add outlines to model-viewer?&lt;label for=&quot;sn-programmerscredo&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-programmerscredo&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;&quot;The Programmers’ Credo: we do these things not because they are easy, but because we thought they were going to be easy&quot;,  &lt;a href=&quot;https://twitter.com/pinboard/status/761656824202276864&quot;&gt;pinboard&lt;/a&gt;&lt;/small&gt;
I have no experience with WebGL, three.js, or postprocessing. I am not sure what a shader is. &lt;a href=&quot;https://www.atomwolf.org/posts/rendering-outlines-with-a-post-processing-shader/&quot;&gt;Let’s find out!&lt;/a&gt;&lt;/p&gt;


&lt;hr&gt;&lt;p&gt;&lt;a href=&quot;mailto:feedcomments@atomwolf.org?subject=Project%20Feed%20Reply%20for%20Experimenting%20with%20CAD%20models%20in%20model-viewer&amp;amp;body=(From%20project%20feed%20entry%3A%20https%3A%2F%2Fwww.atomwolf.org%2Fposts%2Fexperimenting-with-cad-models-in-model-viewer%2F)&quot;&gt;Reply via email&lt;/a&gt;&lt;/p&gt;
		</content>
	</entry>
	<entry>
		<title>Goals for a 3D Model Viewer</title>
		<link href="https://www.atomwolf.org/posts/goals-for-a-3d-model-viewer/"/>
		<updated>2024-03-08T00:00:00Z</updated>
		<id>https://www.atomwolf.org/posts/goals-for-a-3d-model-viewer/</id>
		 <content type="html">
&lt;div class=&quot;dotted-metadata-block&quot;&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Summary&lt;/span&gt;: I list goals for a viewer meant for showcasing 3D CAD models on the web.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Assumed audience&lt;/span&gt;: folks on the web&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Project&lt;/span&gt;: part of &lt;a href=&quot;https://www.atomwolf.org/projects/showcasing-3d-cad-models/&quot;&gt;Showcasing 3D CAD Models on the Web&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;
	&lt;span class=&quot;small-caps&quot;&gt;Tags&lt;/span&gt;:
		&lt;a href=&quot;https://www.atomwolf.org/tags/web/&quot; class=&quot;entry-tag&quot;&gt;web&lt;/a&gt;, 
		&lt;a href=&quot;https://www.atomwolf.org/tags/computers/&quot; class=&quot;entry-tag&quot;&gt;computers&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Created&lt;/span&gt;: &lt;time datetime=&quot;2024-03-08&quot;&gt;08 March 2024&lt;/time&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;hr&gt;&lt;p&gt;My hopes and dreams for a 3D CAD model viewer may be nitpicky and particular, but they’re mine. I’m planning to write about CAD models, and I want to point to details in an interactive web viewer. I started a list of goals when I &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/&quot;&gt;decided to begin with model-viewer&lt;/a&gt;, and I’ve added to it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;annotations (hotspots)&lt;/p&gt;
&lt;p&gt;I need to be able to add a caption to a model feature and to move the camera to show a specific view of an annotation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;component visibility&lt;/p&gt;
&lt;p&gt;I need to be able to hide or show specific components of a model. For instance, I need to be able to show and hide the lid of a box.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;show dimensions&lt;/p&gt;
&lt;p&gt;I need to be able to add the overall dimensions of a model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p id=&quot;banana&quot;&gt;banana for scale&lt;/p&gt;
&lt;p&gt;This is silly, but I’d really like the ability to add a banana next to a model.&lt;label for=&quot;sn-bananaforscale&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-bananaforscale&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;Per &lt;a href=&quot;https://knowyourmeme.com/memes/banana-for-scale&quot;&gt;Know Your Meme&lt;/a&gt;, the earliest instance of a person posting a photo online with a banana for scale was in 2005 on the blog Rock Dog Designs. Using the Wayback Machine, I found the &lt;a href=&quot;https://web.archive.org/web/20060513233537/http://www.rockdogdesigns.com/?p=14&quot;&gt;original post&lt;/a&gt;. &quot;I don’t know how big the screen is, we’re moving and I can’t find the tape measurer. But I do have a banana. For scale. Oh wait, my husband says it’s 19inches [sic].  Oh well, I’ll leave the banana for interest. Please be interested.&quot;&lt;/small&gt;
Other folks have made an &lt;a href=&quot;https://andrewsink.github.io/3D-Banana-for-Scale/&quot;&gt;STL viewer with a banana for scale&lt;/a&gt;, and you can add a &lt;a href=&quot;https://github.com/arturo182/kicad-banana&quot;&gt;banana for scale in KiCad&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Augmented Reality (AR) / Virtual Reality (VR)&lt;/p&gt;
&lt;p&gt;It’d be nice if it were easy to use a mobile device to see what a model looks like on a desk or in a visitor’s particular environment, and it’d be nice if it were easy to use a VR headset to interact with the model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;rendering the models I want to use well&lt;/p&gt;
&lt;p&gt;There’s a lot I don’t know about 3D rendering, and a viewer that does great rendering shoes or models from video games might not automatically do well rendering the CAD models I want to show.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;open source&lt;/p&gt;
&lt;p&gt;I wrote more about why I want to use an open-source viewer in “&lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/&quot;&gt;Choosing a 3D CAD viewer&lt;/a&gt;”, but one reason is that I need to be able to modify the viewer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;not reliant on centralized service or hosting&lt;/p&gt;
&lt;p&gt;I want to be able to host my own models and the viewer itself. If the viewer changes, I don’t want to be forced to quickly update my site. I wrote more about this in “&lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/&quot;&gt;Choosing a 3D CAD viewer&lt;/a&gt;”.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;quick to load&lt;/p&gt;
&lt;p&gt;Adding the viewer to a page shouldn’t make the page slower in a way that people can tell. Real-world web performance is more complicated than &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/overview&quot;&gt;Lighthouse&lt;/a&gt; scores, but the viewer should still allow perfect Lighthouse scores on mobile and desktop.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;low page weight (data)&lt;/p&gt;
&lt;p&gt;If a visitor doesn’t interact with the viewer, the browser shouldn’t need to download much extra data.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;accessibility&lt;/p&gt;
&lt;p&gt;This isn’t a complete list, but it should work well for folks without JavaScript, with vision impairments or with a screen reader, with older browsers, with slower connections, with limited or metered data, and even to folks who like printing things.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;support progressive enhancement&lt;/p&gt;
&lt;p&gt;Adding a 3D viewer to a page shouldn’t break the page for folks who can’t use the viewer. One option has the an image with good alt text and a caption, and the interactive viewer seamlessly replaces the image. If the viewer doesn’t load, the visitor still gets the image.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;next-steps&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/goals-for-a-3d-model-viewer/#next-steps&quot;&gt;Next Steps&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Next, I’m going to load an example CAD model into model-viewer and &lt;a href=&quot;https://www.atomwolf.org/posts/experimenting-with-cad-models-in-model-viewer/&quot;&gt;see how it fares&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;&lt;p&gt;&lt;a href=&quot;mailto:feedcomments@atomwolf.org?subject=Project%20Feed%20Reply%20for%20Goals%20for%20a%203D%20Model%20Viewer&amp;amp;body=(From%20project%20feed%20entry%3A%20https%3A%2F%2Fwww.atomwolf.org%2Fposts%2Fgoals-for-a-3d-model-viewer%2F)&quot;&gt;Reply via email&lt;/a&gt;&lt;/p&gt;
		</content>
	</entry>
	<entry>
		<title>Choosing a 3D CAD Viewer</title>
		<link href="https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/"/>
		<updated>2024-03-06T00:00:00Z</updated>
		<id>https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/</id>
		 <content type="html">
&lt;div class=&quot;dotted-metadata-block&quot;&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Summary&lt;/span&gt;: I investigate 3D model viewers for the web, including &lt;a href=&quot;https://modelviewer.dev/&quot;&gt;model-viewer&lt;/a&gt;, &lt;a href=&quot;https://sketchfab.com/&quot;&gt;Sketchfab&lt;/a&gt;, &lt;a href=&quot;https://naver.github.io/egjs-view3d/&quot;&gt;View3D&lt;/a&gt;, and &lt;a href=&quot;https://threepipe.org/&quot;&gt;ThreePipe&lt;/a&gt;, to showcase CAD models on &lt;span class=&quot;inline-site-name&quot;&gt;ATOM&lt;wbr&gt;WOLF&lt;/span&gt;. I decide to start with &lt;a href=&quot;https://modelviewer.dev/&quot;&gt;model-viewer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Project&lt;/span&gt;: part of &lt;a href=&quot;https://www.atomwolf.org/projects/showcasing-3d-cad-models/&quot;&gt;Showcasing 3D CAD Models on the Web&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Assumed audience&lt;/span&gt;: folks on the web&lt;/p&gt;
&lt;p&gt;
	&lt;span class=&quot;small-caps&quot;&gt;Tags&lt;/span&gt;:
		&lt;a href=&quot;https://www.atomwolf.org/tags/web/&quot; class=&quot;entry-tag&quot;&gt;web&lt;/a&gt;, 
		&lt;a href=&quot;https://www.atomwolf.org/tags/computers/&quot; class=&quot;entry-tag&quot;&gt;computers&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;&lt;span class=&quot;small-caps&quot;&gt;Created&lt;/span&gt;: &lt;time datetime=&quot;2024-03-06&quot;&gt;06 March 2024&lt;/time&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;hr&gt;&lt;p&gt;We’ve been putting 3D models on the web since &lt;a href=&quot;https://en.wikipedia.org/wiki/VRML#Emergence,_popularity,_and_rival_technical_upgrade&quot;&gt;1994&lt;/a&gt;, but doing it well is tricky. The more I learn, the more complicated it seems.&lt;label for=&quot;sn-mithelamodelviewer&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-mithelamodelviewer&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;&lt;a href=&quot;https://mitxela.com&quot;&gt;mitxela&lt;/a&gt; built their own viewer and wrote a &lt;a href=&quot;https://mitxela.com/projects/model-viewer&quot;&gt;good writeup&lt;/a&gt;. Among other things, it reveals a glimpse of the complexity of camera control.&lt;/small&gt;
I want to write articles showcasing CAD models in an interactive web viewer. I looked toward existing options for inspiration.&lt;/p&gt;
&lt;h2 id=&quot;sketchfab&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/#sketchfab&quot;&gt;Sketchfab&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first 3D viewer I found was Sketchfab. Sketchfab says it’s the “best &lt;a href=&quot;https://sketchfab.com/3d-viewer&quot;&gt;3D viewer&lt;/a&gt; on the web”. I don’t know about that, but I do like the annotations.&lt;/p&gt;
&lt;figure&gt;
&lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/images/sketchfab-annotation/&quot; data-lightbox=&quot;true&quot;&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-256w.avif 256w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-410w.avif 410w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-512w.avif 512w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-650w.avif 650w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-850w.avif 850w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1075w.avif 1075w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1206w.avif 1206w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-256w.webp 256w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-410w.webp 410w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-512w.webp 512w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-650w.webp 650w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-850w.webp 850w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1075w.webp 1075w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1206w.webp 1206w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;source type=&quot;image/png&quot; srcset=&quot;https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-256w.png 256w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-410w.png 410w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-512w.png 512w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-650w.png 650w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-850w.png 850w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1075w.png 1075w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1206w.png 1206w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;img alt=&quot;A 3D model of a bust of Nefertiti, marked with a number and a floating box with text above an image with sunglasses. The text reads &amp;quot;We can also add images and GIFs using Markdown. The one below is being pulled in from giphy.com.&amp;quot;&quot; loading=&quot;eager&quot; src=&quot;https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-256w.jpeg&quot; width=&quot;1206&quot; height=&quot;1408&quot; srcset=&quot;https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-256w.jpeg 256w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-410w.jpeg 410w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-512w.jpeg 512w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-650w.jpeg 650w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-850w.jpeg 850w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1075w.jpeg 1075w, https://www.atomwolf.org/img/sketchfab-annotation-i6h7zmiuoB-1206w.jpeg 1206w&quot; sizes=&quot;(min-width: 1280px) calc(50vw - 84px), (min-width: 780px) calc(55vw - 84px), (min-width: 640px) calc(80vw - 82px), calc(90vw - 82px)&quot;&gt;&lt;/picture&gt;&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;A model annotation from the &lt;a href=&quot;https://sketchfab.com/3d-models/sketchfab-annotations-demo-a81a3dbe18784734bc11b2725cc4044&quot;&gt;Sketchfab Annotation Demo&lt;/a&gt;&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If I were to use Sketchfab, Sketchfab would host my 3D models. I’d add an &lt;code&gt;iframe&lt;/code&gt; with a particular Sketchfab URL to a page, and web browsers would create a page-within-a-page to show the viewer. Sketchfab has a JavaScript &lt;a href=&quot;https://sketchfab.com/developers/viewer&quot;&gt;API&lt;/a&gt; that lets developers interact with the viewer in the &lt;code&gt;iframe&lt;/code&gt; to develop some custom features.&lt;/p&gt;
&lt;p&gt;Sketchfab’s viewer is hosted by Sketchfab, and my site would just refer to it. If Sketchfab changed their viewer, the viewers on my pages would change, whether I wanted it or not. If Sketchfab had an outage, my pages would be broken. If Sketchfab went out of business, I’d have to scramble to replace them. If the Sketchfab viewer were open source or if I could host it, I’d be less concerned.&lt;label for=&quot;sn-emergencies&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-emergencies&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;I&#39;ve found that relying on this type of service tends to create emergencies for me. The more services like this I use, the more frequently my work is driven by upstream changes. Having something I host, something I update, helps reduce those emergencies. Being able to use a previous version is really helpful!&lt;/small&gt;
Furthermore, my ability to modify the viewer is limited.&lt;/p&gt;
&lt;p&gt;For the features I’d like, Sketchfab seems expensive. I think Sketchfab would cost me at least $129 per month.&lt;/p&gt;
&lt;p&gt;I don’t think Sketchfab is a good fit for a 3D viewer for &lt;span class=&quot;inline-site-name&quot;&gt;ATOM&lt;wbr&gt;WOLF&lt;/span&gt;, but it may be a good fit for others. If I were advising a business that sold 3D models needed to get a 3D viewer out ASAP, I’d check out Sketchfab.&lt;/p&gt;
&lt;h2 id=&quot;autodesk-viewer&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/#autodesk-viewer&quot;&gt;Autodesk Viewer&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Autodesk has a &lt;a href=&quot;https://viewer.autodesk.com/&quot;&gt;web viewer&lt;/a&gt;. I can save my Fusion 360 projects to their storage and look at them in the browser. The viewer displays the model to look just like it does in Fusion 360. You can rotate and zoom, measure parts of the model, run saved animations, and show and hide parts of the model. I can make the project public and embed the viewer into a page.&lt;/p&gt;
&lt;p&gt;The Autodesk viewer wouldn’t cost me anything extra, but my other concerns about Sketchfab apply here.&lt;/p&gt;
&lt;h2 id=&quot;goals&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/#goals&quot;&gt;Goals&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After Sketchfab and the Autodesk viewer, I had a better sense of what I wanted.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I don’t want to use a viewer that’s an &lt;code&gt;iframe&lt;/code&gt; to an external site or one that only shows models hosted at an external site. I want to be able to host the models and the viewer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I want to be able to modify the viewer. It should be open source.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I need to add text to call out model features to discuss design decisions. These are called “annotations” or “hotspots”.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After surveying more viewer options, I grouped them into categories.&lt;/p&gt;
&lt;h2 id=&quot;framework-viewers&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/#framework-viewers&quot;&gt;Framework viewers&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Framework viewers are more like libraries or examples than applications. They handle many complexities but would require development work to integrate into a site.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://threejs.org/&quot;&gt;Three.js&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://threejs.org/&quot;&gt;Three.js&lt;/a&gt; is an open-source JavaScript library for 3D graphics in the browser. It uses WebGL, and it can use WebXR to do &lt;a href=&quot;https://threejs.org/docs/#manual/en/introduction/How-to-create-VR-content&quot;&gt;Virtual Reality&lt;/a&gt; (VR) things. It has &lt;a href=&quot;https://threejs.org/docs/index.html&quot;&gt;good docs&lt;/a&gt;, a &lt;a href=&quot;https://threejs.org/examples/&quot;&gt;bunch of examples&lt;/a&gt;, and it’s been around since 2010. It’s pretty awesome. Most of the other options on this page are either built on three.js or built on things built on three.js.&lt;/p&gt;
&lt;p&gt;I’m not sure three.js has a viewer.  They have an &lt;a href=&quot;https://threejs.org/editor/&quot;&gt;editor&lt;/a&gt; and &lt;a href=&quot;https://threejs.org/examples/?q=loader&quot;&gt;many loader examples&lt;/a&gt;. I’d definitely have to build a lot of the features I want.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://aframe.io/&quot;&gt;A-Frame&lt;/a&gt;’s &lt;a href=&quot;https://aframe.io/aframe/examples/showcase/model-viewer/&quot;&gt;model viewer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://aframe.io/&quot;&gt;A-Frame&lt;/a&gt; is a fun web framework built on top of three.js, meant for VR. My first VR development experience used A-Frame. I made a demo that played videos and used a controller for 3D space data logging. After getting through some conceptual hoops around VR development, using A-Frame was really quick. I liked it, but I don’t think I’d use A-Frame for this project.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.babylonjs.com/&quot;&gt;Babylon.js&lt;/a&gt;’s &lt;a href=&quot;https://doc.babylonjs.com/features/featuresDeepDive/babylonViewer&quot;&gt;viewer&lt;/a&gt; (&lt;a href=&quot;https://codepen.io/BabylonJS/pen/QxzBPd/&quot;&gt;example&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.babylonjs.com/&quot;&gt;Babylon.js&lt;/a&gt; is similar to three.js, but the library is a bit less graphics-y and a little more “game engine-y”. The Babylon.js model viewer has a lot of features: animations, asynchronous loading of models, the &lt;a href=&quot;https://doc.babylonjs.com/toolsAndResources/inspector&quot;&gt;Babylon.js Inspector&lt;/a&gt;. I didn’t see annotations, and the interface would need to be modified.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;viewer-applications&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/#viewer-applications&quot;&gt;Viewer “applications”&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These viewers are meant to be used as-is. They vary widely, and I gave some only a glance.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Don McCurdy’s &lt;a href=&quot;https://gltf-viewer.donmccurdy.com/&quot;&gt;three-gltf-viewer&lt;/a&gt; (&lt;a href=&quot;https://github.com/donmccurdy/three-gltf-viewer&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;This viewer is built on three.js. It supports animations but doesn’t appear to support annotations or VR/AR/WebXR.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;kovacsv’s &lt;a href=&quot;https://3dviewer.net/&quot;&gt;Online3DViewer&lt;/a&gt; (&lt;a href=&quot;https://github.com/kovacsv/Online3DViewer&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;I don’t know a lot about kovacsv’s Online3DViewer.  It’s open source and uses three.js.  The viewer is embedded using an iframe.  I don’t think it has annotations or VR/AR/WebXR support.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://naver.github.io/egjs-view3d/&quot;&gt;View3D&lt;/a&gt; (&lt;a href=&quot;https://github.com/naver/egjs-view3d&quot;&gt;GitHub&lt;/a&gt;)&lt;br&gt;
View3D is built on three.js. It’s open source. It supports annotations and animations. It supports augmented reality through WebXR, Android’s Scene Viewer, and iOS’s AR Quick Look. It appears to be maintained by a South Korean company called &lt;a href=&quot;https://en.wikipedia.org/wiki/Naver&quot;&gt;Naver&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://threepipe.org/&quot;&gt;ThreePipe&lt;/a&gt; (&lt;a href=&quot;https://github.com/repalash/threepipe&quot;&gt;GitHub&lt;/a&gt;))&lt;/p&gt;
&lt;p&gt;ThreePipe is built on three.js. ThreePipe says it has a “simple, intuitive API for creating 3D model viewers/configurators/editors on web pages, with many built-in presets for common workflows and use-cases.” It has plugins, including one for &lt;a href=&quot;https://github.com/repalash/threepipe?tab=readme-ov-file#threepipeplugin-gaussian-splatting&quot;&gt;Gaussian splats&lt;/a&gt;.&lt;label for=&quot;sn-splats&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-splats&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Gaussian_splatting&quot;&gt;Gaussian splats&lt;/a&gt; don&#39;t seem helpful for this project, but they&#39;re interesting!&lt;/small&gt;
It doesn’t appear to support WebXR/AR/VR yet, and it doesn’t appear to support annotations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://modelviewer.dev/&quot;&gt;model-viewer&lt;/a&gt; (&lt;a href=&quot;https://github.com/google/model-viewer&quot;&gt;GitHub&lt;/a&gt;, &lt;a href=&quot;https://modelviewer.dev/docs/&quot;&gt;docs&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;model-viewer is a Google project. It’s open source and built on three.js. It’s got a lot of &lt;a href=&quot;https://modelviewer.dev/examples/loading/&quot;&gt;examples&lt;/a&gt;. It supports &lt;a href=&quot;https://modelviewer.dev/examples/annotations/index.html&quot;&gt;annotations&lt;/a&gt;, and &lt;a href=&quot;https://modelviewer.dev/examples/augmentedreality/&quot;&gt;AR&lt;/a&gt; through &lt;a href=&quot;https://modelviewer.dev/docs/faq.html#entrydocs-general-questions-ar&quot;&gt;WebXR, AR Quick Look, and Scene Viewer&lt;/a&gt;. VR support seems to be in progress. It has an &lt;a href=&quot;https://modelviewer.dev/examples/annotations/index.html#dimensions&quot;&gt;example showing model dimensions&lt;/a&gt;. There is good integration between the 3D viewer and the rest of the webpage. It feels “in the spirit of the web”.&lt;/p&gt;
&lt;p&gt;model-viewer has a lot of polish. It can show an “&lt;a href=&quot;https://modelviewer.dev/docs/index.html#entrydocs-stagingandcameras-attributes-interactionPrompt&quot;&gt;interaction prompt&lt;/a&gt;” animation on the model, giving visitors an affordance that the model isn’t a static image. model-viewer has an &lt;a href=&quot;https://modelviewer.dev/editor/&quot;&gt;editor&lt;/a&gt; to help with configuration. The model-viewer folks care about and track [page load performance].(&lt;a href=&quot;https://modelviewer.dev/examples/lighthouse.html&quot;&gt;https://modelviewer.dev/examples/lighthouse.html&lt;/a&gt;)&lt;label for=&quot;sn-pageload&quot; class=&quot;margin-toggle sidenote-number&quot;&gt;&lt;/label&gt;
&lt;input type=&quot;checkbox&quot; id=&quot;sn-pageload&quot; class=&quot;margin-toggle&quot;&gt;
&lt;small class=&quot;sidenote&quot;&gt;Asynchronously loading model-viewer decreases its impact on page load performance, but model-viewer is still 60+ KB. &lt;a href=&quot;https://lite-model-viewer.netlify.app/&quot;&gt;Lite model-viewer&lt;/a&gt; is an easy-to-use third-party wrapper that defaults to only loading model-viewer after the visitor interacts with the model image. For a visitor that isn&#39;t going to interact with the model, using lite-model-viewer means the page size is only around 3 KB larger than just showing the image!&lt;/small&gt;
model-viewer starts with a pre-rendered image of the scene and seamlessly replaces it with the interactive model when the model file loads. This is “in the spirit of the web” and heavily influenced my decision to start with model-viewer.&lt;/p&gt;
&lt;p&gt;Another way model-viewer aligns with the web is that it’s a Web Component. You don’t need to write JavaScript to put a model on your page. You can add a &lt;code&gt;&amp;lt;model-viewer&amp;gt;&lt;/code&gt; tag. Awesome!&lt;/p&gt;
&lt;p&gt;model-viewer is open to modifications. &lt;a href=&quot;https://modelviewer.dev/examples/postprocessing/index.html&quot;&gt;model-viewer-effects&lt;/a&gt; provides postprocessing effects, like pixelation, glitching, and antialiasing. Additionally, I could &lt;a href=&quot;https://modelviewer.dev/docs/faq.html#entrydocs-general-questions-three&quot;&gt;hook into three.js and modify things how I like&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt; &lt;a href=&quot;https://www.atomwolf.org/posts/choosing-a-3d-cad-viewer/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’m going to start with model-viewer, but not without reservation.&lt;/p&gt;
&lt;p&gt;model-viewer is maintained by folks from Google. While I’m cautious of &lt;a href=&quot;https://killedbygoogle.com/&quot;&gt;Google’s history of discontinuing projects&lt;/a&gt;, the open-source nature of model-viewer gives me reassurance. When Google decides it’s done with model-viewer, the community might maintain it, but I could continue to use the last version while migrating to another option, like &lt;a href=&quot;https://naver.github.io/egjs-view3d/&quot;&gt;View3D&lt;/a&gt; or &lt;a href=&quot;https://threepipe.org/&quot;&gt;ThreePipe&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My next step is to &lt;a href=&quot;https://www.atomwolf.org/posts/goals-for-a-3d-model-viewer&quot;&gt;discover the rest of my finicky requirements&lt;/a&gt; as I integrate model-viewer into &lt;span class=&quot;inline-site-name&quot;&gt;ATOM&lt;wbr&gt;WOLF&lt;/span&gt;.&lt;/p&gt;
&lt;hr&gt;&lt;p&gt;&lt;a href=&quot;mailto:feedcomments@atomwolf.org?subject=Project%20Feed%20Reply%20for%20Choosing%20a%203D%20CAD%20Viewer&amp;amp;body=(From%20project%20feed%20entry%3A%20https%3A%2F%2Fwww.atomwolf.org%2Fposts%2Fchoosing-a-3d-cad-viewer%2F)&quot;&gt;Reply via email&lt;/a&gt;&lt;/p&gt;
		</content>
	</entry>
</feed>
