<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nikita Nafranets</title>
    <description>The latest articles on DEV Community by Nikita Nafranets (@dimensi).</description>
    <link>https://dev.to/dimensi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F662719%2F06153273-c68c-4c32-9439-5a6488bdfc9c.jpeg</url>
      <title>DEV Community: Nikita Nafranets</title>
      <link>https://dev.to/dimensi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dimensi"/>
    <language>en</language>
    <item>
      <title>Знакомство фронтендера с WebGL: четкие линии</title>
      <dc:creator>Nikita Nafranets</dc:creator>
      <pubDate>Tue, 06 Jul 2021 22:39:46 +0000</pubDate>
      <link>https://dev.to/dimensi/weggl-3-3k58</link>
      <guid>https://dev.to/dimensi/weggl-3-3k58</guid>
      <description>&lt;h2&gt;
  
  
  Свой .obj парсер, свой webgl
&lt;/h2&gt;

&lt;p&gt;Первое, что я сделал адаптировал код из песочницы и использовал gl.LINES.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--H-hAzScR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4xm3c19cefflyds938e.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--H-hAzScR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l4xm3c19cefflyds938e.jpg" alt="ужасная песочница"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(извиняюсь за качество, код с той песочницей потерял и даже по памяти результат восстановить не могу)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Показав дизайнеру, я ожидал услышать, что все идеально/перфектно, ты отлично поработал! Но услышал:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Выглядит круто! А теперь добавь текстуры, модель не должна просвечиваться...  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;И тут я понял, что &lt;code&gt;gl.LINES&lt;/code&gt; мне никак не помогут для решения задачи, я просто пошел не совсем туда. Мне почему-то казалось, что самое важное это линии, но потом понял, что должен был залить цветом модельку и выделить на ней грани поверхностей другим цветом.&lt;/p&gt;

&lt;p&gt;Я понял, что мне все же нужны uv (текстурные координаты), потому, что без них невозможно закрашивать фигуру правильно, но те uv который генерировал редактор моделей не подходили под закрашивание. Там была какая-та своя логика по генерации координат. &lt;/p&gt;

&lt;p&gt;Подняв этот вопрос с человеком который показал парсинг. Он мне дал новую песочницу в которой показал, как генерировать текстурные координаты, чем вселил новую надежду. Он так же набросал простейший шейдер который рисовал линии. Взял его решение, я обновил свою песочницу и обновил парсер.&lt;br&gt;
Код парсера в статье я покажу впервые.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uv4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt; &lt;span class="c1"&gt;// захаркоженные координаты текстур&lt;/span&gt;

&lt;span class="c1"&gt;// функция которая парсит .obj и выплевывает вершины с текстурными координатами.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getVBForVSTFromObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preLines&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[\r\n]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// функция которая отдавала все строки по первому вхождению&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exNs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fchar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;a&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;fchar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;s&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// та же функция что выше, только для поверхностей (faces) и дополнительно парсила сами поверхности&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;exFs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;s&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;f&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;s&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vertexList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exNs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preLines&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;v&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// получаем все вершины&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;faceList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;exFs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preLines&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// все поверхности&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filteredFaceList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;faceList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// собираем поверхности только с 4 точками, т.е. квады&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vertexes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;filteredFaceList&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;vertexList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;v0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v3&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// склеиваем треугольники &lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uvs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filteredFaceList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uv4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;uv4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;uv4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;uv4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;uv4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;uv4&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
  &lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// собираем текстурные координаты под каждую поверхность&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;vertexes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;uvs&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Дальше, я обновил у себя фрагментный шейдер:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;varying&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// текстурные координаты из вершинного шейдера&lt;/span&gt;
&lt;span class="c1"&gt;// define позволяет определять константы&lt;/span&gt;
&lt;span class="cp"&gt;#define FN (0.07) // толщина линии, просто какой-то размер, подбирался на глаз
#define LINE_COLOR vec4(1,0,0,1) // цвет линии. красный.
#define BACKGROUND_COLOR vec4(1,1,1,1) // остальной цвет. белый.
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; 
    &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;FN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;FN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;FN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;FN&lt;/span&gt; 
  &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;// если мы находимся на самом краю поверхности, то рисуем выставляем цвет линии&lt;/span&gt;
    &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;LINE_COLOR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; 
    &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;BACKGROUND_COLOR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(&lt;a href="https://codesandbox.io/s/apple-webgl-jd0kq"&gt;песочница&lt;/a&gt;)&lt;br&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MiiwvhlR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xcnw797glc4u3ppvgcf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MiiwvhlR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5xcnw797glc4u3ppvgcf.png" alt="мои любимые линии"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
И, о боже! Вот он результат который я так хотел. Да грубо, линии жесткие, но это шаг вперед. Дальше я переписал код шейдера на &lt;code&gt;smoothstep&lt;/code&gt; (специальная функция которая позволяет делать линейную интерполяцию) и поменял еще стиль нейминга переменных.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;uLineColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// теперь цвета и прочее передаю js, а не выставляю константы&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;vec3&lt;/span&gt; &lt;span class="n"&gt;uBgColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;varying&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// функция которая высчитала на основе uv и "порога" и сколько должна идти плавность&lt;/span&gt;
&lt;span class="c1"&gt;// то есть через threshold я говорил где должен быть один цвет, а потом начинается другой, а с помощью gap определял долго должен идти линейный переход. Чем выше gap, тем сильнее размытость.&lt;/span&gt;
&lt;span class="c1"&gt;// и которая позволяет не выходить за пределы от 0 до 1&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;calcFactor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.,&lt;/span&gt; 
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calcFactor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;threshold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// функция mix на основе 3 аргумента выплевывает 1 аргумент или 2, линейно интерпретируя.&lt;/span&gt;
  &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uLineColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.),&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uBgColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;factor&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DVYKcMWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/im5k5g14e1jtyu3whwux.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DVYKcMWi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/im5k5g14e1jtyu3whwux.png" alt="Все, я свободен, где моя зп?"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
И вот, красота! Дизайнер доволен и я тоже. Да есть какие-то мелочи, но это лучшее что я смог тогда родить.&lt;br&gt;&lt;br&gt;
Хотя особо внимательные сразу заметят, что размеры квадратов стали больше, чем на прошлой "грубой" версии.&lt;br&gt;
А я был не особо внимательным, поэтому заметил это только спустя 2 недели. Возможно, эйфория от успеха вскружила мне голову...&lt;/p&gt;
&lt;h3&gt;
  
  
  Доработка шейдера
&lt;/h3&gt;

&lt;p&gt;Когда я закончил первую реализация рендера, я пошел делать другие задачи по проекту. Но в течении 2 недель, я понял, что недоволен тем как выглядит модель, они точно не выглядели как на рендере у дизайнера, да еще меня беспокоило, что я толщина линий все равно была какой-то не такой.  &lt;/p&gt;

&lt;p&gt;Мне было не понятно, почему у меня такая крупная сетка на яблоке, хотя в cinema4d и блендер, она довольно мелкая. &lt;br&gt;
Плюс, я решил поделиться со своими переживаниями с коллегой на работе, и когда я ему начал объяснять как работает мой шейдер, я понял, что уже и не помню как я вообще до него допер и при попытке объяснить ему, я начал по новой экспериментировать с шейдером.&lt;br&gt;&lt;br&gt;
Для начала я вспомнил трюк из &lt;a href="https://www.youtube.com/watch?v=u5HAYVHsasc&amp;amp;list=PLGmrMu-IwbguU_nY2egTFmlg691DN7uE5"&gt;уроков по шейдерам&lt;/a&gt; и просто закидывал цвета на основе x координаты и получил для себя интересный результат.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ncAna96X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6ah9gjcb0qjiu3a8x4i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ncAna96X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6ah9gjcb0qjiu3a8x4i.jpg" alt="Выглядит модно же"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Я понял, что все это время у меня были вся эта мелкая сетка, но я почему-то игнорировал ее. Поиграв еще, я наконец-то понял, что зарисовал только 2 грани из 4 у каждой поверхности, что привело к тому, что у меня такая крупная сетка.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mQzHIJxb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3e3muxpnrblpepnz3k1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mQzHIJxb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a3e3muxpnrblpepnz3k1.jpg" alt="Слишком крупная сетка"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;У меня не получалось используя степы и прочее, реализовать нужную мне сетку, я получал какой-то бред.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5WHMSduK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/73ysuyzffrs8jd2ct0h7.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5WHMSduK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/73ysuyzffrs8jd2ct0h7.jpg" alt="Лучше сетки, только точки"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Тогда, я решил сначала написать топорно и родил такой шейдер.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uBgColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uLineColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Я наконец-то получил нужный результат.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wHDDMN43--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cl9y6j4fblcn9jupfyvk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wHDDMN43--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cl9y6j4fblcn9jupfyvk.jpg" alt="Перфекто"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Дальше, за час вместе с докой по функциям из webgl. Я смог переписать код на более близкий к webgl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;border&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;xy0&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;xy1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;vec2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xy0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;xy1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;uv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;fw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uLineWidth&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;br&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;border&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;vTextureCoords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uLineWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fw&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uLineColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;uBgColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;br&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Я получил мелкую сетку. &lt;strong&gt;Ура!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VXmfl5kZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/klh1gjh1pwn3j6di0aja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VXmfl5kZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/klh1gjh1pwn3j6di0aja.png" alt="Мама мия"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Но, у меня оставалась проблема, что чем ближе к краю, тем хуже различаются линии.&lt;br&gt;
Насчет этого вопроса, я обратился за помощью в чат и мне рассказали про &lt;code&gt;OES_standard_derivatives&lt;/code&gt; экстеншена для webgl. Это что-то вроде плагинов, которые добавляли в glsl новые функции или включали какие-то возможности в рендере. Добавив в код шейдера fwidth (не забывайте включать экстеншены, до того как соберете программу, а то буду проблемы), функцию которая появилась после подключение экстеншена. Я добился того, чего хотел.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;  &lt;span class="cp"&gt;#ifdef GL_OES_standard_derivatives
&lt;/span&gt;    &lt;span class="n"&gt;fw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fwidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Боже, какая красота!&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1UhexExH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9nlr9ym60gnfn7kns72.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1UhexExH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9nlr9ym60gnfn7kns72.jpg" alt="Лучше не смогу"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Осталось только написать как я делал анимацию!&lt;/p&gt;

</description>
      <category>webgl</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Знакомство фронтендера с WebGL: первые наброски</title>
      <dc:creator>Nikita Nafranets</dc:creator>
      <pubDate>Tue, 06 Jul 2021 22:31:31 +0000</pubDate>
      <link>https://dev.to/dimensi/weggl-2-3o0m</link>
      <guid>https://dev.to/dimensi/weggl-2-3o0m</guid>
      <description>&lt;h2&gt;
  
  
  Вспоминаем ради чего мы начали изучать WebGL
&lt;/h2&gt;

&lt;p&gt;После недельного чтения ресурса и экспериментов у меня появился кустарный REPL, в котором я мог быстро набрасывать шейдеры и другой код, чтоб поэкспериментировать и найти решение.  &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Я пишу статью, спустя 2 месяца после того как закончил работу над задачей и осталась только такая песочница -&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/apple-webgl-u4pg7"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Вооружись знаниями и реплом, я пошел искать то, что сможет распарсить мне .obj файлик на вершины.&lt;br&gt;
Единственное в интернете которое более/менее правильно распарсило мне файлик это был npm пакет&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/webgl-obj-loader"&gt;webgl-obj-loader&lt;/a&gt;. Хотя в нем был досадный баг который попортил мне немало крови.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/frenchtoast747/webgl-obj-loader/pull/66"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--i3JOwpme--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Mesh: Fix bug with triangulation
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#66&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/qtip"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q98-LueG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars.githubusercontent.com/u/307850%3Fv%3D4" alt="qtip avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/qtip"&gt;qtip&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/frenchtoast747/webgl-obj-loader/pull/66"&gt;&lt;time&gt;Oct 30, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;The triangulate function splits an ngon into individual triangles. This
commit fixes a bug where the mesh parsing code was ignoring the emitted
triangles and instead using the original ngon indices.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/frenchtoast747/webgl-obj-loader/pull/66"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
 
&lt;h3&gt;
  
  
  Первые наброски
&lt;/h3&gt;

&lt;p&gt;С помощью библиотеки, я сразу смог добиться какого-то результата в своей песочнице.&lt;/p&gt;

&lt;p&gt;Vertex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;attribute&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// объявляем переменную в которую будем прокидывать вершины яблока.&lt;/span&gt;

&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;u_matrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// матрица которая будет нам помогать трансформировать модель&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u_matrix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// у glsl есть встроенные возможности по работе с матрицами. Тут он сам за нас перемножает вершины на матрицы и тем самым смещает их куда надо.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fragment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// точность для округления. &lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.);&lt;/span&gt; &lt;span class="c1"&gt;// заливаем красным&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Сам код&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vertex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shaders&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// через parcel импортирует тексты&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createCanvas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createProgramFromTexts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./helpers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;m4&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./matrix3d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// после изучение webgl на webgl fund, мне в наследство досталась библиотека которая умеет работает с 3д матрицами.&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;appleObj&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./apple.obj&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// моделька яблока&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;OBJ&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webgl-obj-loader&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// наша либа которая распарсит obj&lt;/span&gt;


&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apple&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;OBJ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Mesh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appleObj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// загружаем модель&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createCanvas&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// создаю canvas и вставляю в body&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;webgl&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// получаю контекст&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;program&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createProgramFromTexts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;vertex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fragment&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// создаю программу из шейдеров&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useProgram&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// линкую программу к контексту&lt;/span&gt;

  &lt;span class="c1"&gt;// получаю ссылку на атрибут&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;positionLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a_position&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// у либы была готовая функция, которая за меня создавала буфер и прокидывала распарсенные данные в буферы. Из .obj можно было достать не только вершины, но и другие координаты которые могут быть полезны.&lt;/span&gt;
  &lt;span class="nx"&gt;OBJ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;initMeshBuffers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enableVertexAttribArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;positionLocation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// активирую атрибут, зачем это делать не знаю, но не сделаешь, ничего не заработает.&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vertexAttribPointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;positionLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vertexBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;itemSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// либа сама определяла сколько нужно атрибуту брать чисел, чтоб получить вершину&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// отключаем нормализацию (это чтоб не пыталось конвертировать числа больше 1 в 1. Аля 255 -&amp;gt; 0.255.&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// объясняю как атрибуту парсить данные&lt;/span&gt;

  &lt;span class="c1"&gt;// получаем ссылку на глобальную переменную которая будет доступна внутри шейдеров. В нее же мы будем прокидывать матрицы&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matrixLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUniformLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;u_matrix&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;translation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// смещаю на центр экрана по вертикали и 400 px вниз&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;degToRad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;degToRad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;degToRad&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt; &lt;span class="c1"&gt;// вращение по нулям&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;scale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// увеличиваю модельку в 5 раз. scaleX, scaleY, scaleZ&lt;/span&gt;

  &lt;span class="c1"&gt;// выставляю вью порт&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEPTH_TEST&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// включаем специальный флаг, который заставляет проверять видеокарту уровень вложенности и если какой-то треугольник перекрывает другой, то другой не будет рисоваться, потому, что он не виден.&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;drawScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;COLOR_BUFFER_BIT&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEPTH_BUFFER_BIT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// очищаем канвас на каждый рендер&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matrix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;multiply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;identity&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// создаем единичную матрицу. Матрицу у которой все значения по умолчанию.&lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;orthographic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;canvas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Создаем матрицу которая конвертирует неудобные размеры модельки яблока в координатное пространство -1 до 1.&lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;translation&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// перемещаем модельку &lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;xRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;// крутим по X&lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;yRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;// крутим по Y&lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="c1"&gt;// крутим по Z&lt;/span&gt;
      &lt;span class="nx"&gt;m4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scaling&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// увеличиваем модельку&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// перемножаем матрицы друг на друга, чтоб в конце получить 1 матрицу которую и прокинем в шейдер&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniformMatrix4fv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matrixLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;matrix&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// прокидываем матрицу&lt;/span&gt;
    &lt;span class="c1"&gt;// подключаю буфер с индексами&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bindBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ELEMENT_ARRAY_BUFFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// рисуем яблоко треугольниками с помощью индексов&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;drawElements&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TRIANGLES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;indexBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;numItems&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNSIGNED_SHORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;drawScene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Тут код который настраивает всякие слайдеры, чтоб изменять матрицы.&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;На выход я получил такую штуку:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i5YyfaJg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7a2q3ql47vc94bj5ihoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i5YyfaJg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7a2q3ql47vc94bj5ihoy.png" alt="почти результат"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Мне кажется это маленький шаг для webgl, но огромный скачок для &lt;strong&gt;фронтендера&lt;/strong&gt;.  &lt;/p&gt;
&lt;h3&gt;
  
  
  Что такое &lt;strong&gt;index&lt;/strong&gt;?
&lt;/h3&gt;

&lt;p&gt;При работе с либой узнал про новую вещь: индексы.&lt;br&gt;
На самом деле в .obj файле кроме вершин есть текстурные координаты, нормали, поверхности (faces).&lt;br&gt;
Что это вообще такое?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Текстурные координаты это массив цифр который прокидывается в фрагментный шейдер и позволяет шейдеру понять где он вообще сейчас находится в модельке, чтоб наложить пиксель в зависимости от общего положения. Если их нет, то шейдер получается вообще изолированным и может только красить пиксели не зная где именно сейчас он закрашивает.
Текстурные координаты прокидываются как атрибуты.&lt;/li&gt;
&lt;li&gt;Нормали это тоже координаты, но их можно использовать в фрагментом шейдере, чтоб понять как рисовать тень от объекта(модель) в зависимости от того как должен падать свет на объект.&lt;/li&gt;
&lt;li&gt;Поверхность - это массив индексов которые указывают на индекс в массиве вершин, текстур и нормалей. Поверхности это служебные данные для редактора моделей (аля cinema4d и других), которые позволяют объединять полигоны в квадраты и другие более сложные фигуры. В частности это нужно для того как рендерить именно модельку.
Так вот индексы это и есть поверхности. Допустим мы прокинули в 2 атрибута данные от вершин и текстурных координат. И webgl смотрит на текущий индекс и по параметрам атрибутов (помните мы указывали size, сколько нужно брать чисел, чтоб получить вершину) берет из каждого атрибута нужный набор чисел и прокидывает их в шейдеры.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Дальше я попробовал изменить &lt;code&gt;gl.TRIANGLES&lt;/code&gt; на &lt;code&gt;gl.LINES&lt;/code&gt;. И получил следующий результат:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HcNTvp3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jx0yc3n5yjbcx2gq36xp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HcNTvp3j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jx0yc3n5yjbcx2gq36xp.png" alt="еще чуть-чуть"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Мда, совершенно не то, что я ожидал. Где мои красивые линии как у дизайнера и че за треугольники. Я тогда впервые осознал простую истину, что все блин на треугольниках. В данной ситуации, я побежал в чат и тогда породил локальный мем.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;у меня начали появляться подозрения, что рисовать фигуры можно только через треугольники.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Я просто не знал что делать дальше и спрашивал советов. Среди них было несколько:&lt;br&gt;&lt;br&gt;
- Используй в фрагментом шейдере uv, чтоб рисовать линии сам.&lt;br&gt;&lt;br&gt;
- Распарси .obj сам и получили нужные значения.&lt;br&gt;&lt;br&gt;
- Сделай uv разветку и натяну текстуру картинку.&lt;/p&gt;

&lt;p&gt;Я не понял из 1 ответа, что такое uv, почему-то тогда мне никто не объяснил, что это и есть текстурные координаты. Да и где эти uv брать тоже было не понятно.&lt;br&gt;&lt;br&gt;
Из второе ответа, я тоже не понял что мне делать и какие значения использовать.&lt;br&gt;&lt;br&gt;
А третий ответ оказался хоть тоже загадочным, но мне объяснили что это значит. Нужно было через редактор моделей создать текстурные координаты и нарисовать под них текстуру.&lt;br&gt;&lt;br&gt;
В интернете я нашел гайды о том как сделать в cinema 4d uv разметку и там же нашел как нарисовать текстуру. В редакторе была возможность создать картинку и залить по граням поверхностей(faces) нужный цвет. Я считал, что это сразу решает мою проблему. Выплюнув texture.png и новый obj с uv (то есть так называются текстурные координаты).  &lt;/p&gt;
&lt;h2&gt;
  
  
  Баг который попортил нервы
&lt;/h2&gt;

&lt;p&gt;Я побежал читать статью на &lt;a href="https://webglfundamentals.org/webgl/lessons/ru/webgl-3d-textures.html"&gt;webgl fund как натянут текстуру&lt;/a&gt;. Кода стало больше, но не увидел сложностей. Сделал как в гайде и думал, щас будет все отлично!  &lt;/p&gt;

&lt;p&gt;Vertex&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;attribute&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;attribute&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;a_texture_coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// текстурные координаты из модели&lt;/span&gt;

&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;mat4&lt;/span&gt; &lt;span class="n"&gt;u_matrix&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;varying&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u_matrix&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="n"&gt;v_texture_coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_texture_coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// прокидываем во фрагментный шейдер &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fragment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;varying&lt;/span&gt; &lt;span class="kt"&gt;vec2&lt;/span&gt; &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// координаты из вершины&lt;/span&gt;
&lt;span class="k"&gt;uniform&lt;/span&gt; &lt;span class="kt"&gt;sampler2D&lt;/span&gt; &lt;span class="n"&gt;u_texture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// текстура&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;texture2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;u_texture&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v_texture_coords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textureCoordsLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAttribLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a_texture_coords&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// получили ссылку на новый атрибут&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enableVertexAttribArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textureCoordsLocation&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bindBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ARRAY_BUFFER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textureBuffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// забиндили буфер которая выдала либа из модели&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vertexAttribPointer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;textureCoordsLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;apple&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textureBuffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;itemSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FLOAT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;texture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createTexture&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// запрашиваем место для текстуры&lt;/span&gt;
  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bindTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// биндим&lt;/span&gt;

  &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texImage2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNSIGNED_BYTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// сначала прокидываем пустышку, пока грузится текстура&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;textureImg&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// загружаем текстуру&lt;/span&gt;
  &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bindTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texImage2D&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RGBA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;UNSIGNED_BYTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;generateMipmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texParameteri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_MIN_FILTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LINEAR_MIPMAP_LINEAR&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;texParameteri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_2D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TEXTURE_MAG_FILTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LINEAR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// какие-то неведомые настройки, чтоб все было круто&lt;/span&gt;
    &lt;span class="nx"&gt;drawScene&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;textureLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getUniformLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;program&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;u_texture&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;drawScene&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nx"&gt;gl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniform1i&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;textureLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;И после тонны кода получаем вот это &lt;strong&gt;чудовище&lt;/strong&gt;:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i2EZv7rZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xyxuh1qfsf7axfn43785.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i2EZv7rZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xyxuh1qfsf7axfn43785.png" alt="шаг назад"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Что за??&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
И вот тут у меня началась эпопея в целый рабочий день с целью решить проблему. Я мало, что понимал и считал, что это я косячу, а не либа которую использую. Сначала я на самом деле откатил код с текстурой и просто попробовал закрасить и получил опять какой-то невероятный результат.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3eJOPMs_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nky6y6i5be12nejas5f2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3eJOPMs_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nky6y6i5be12nejas5f2.png" alt="боже спасите меня"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Что за фигня?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Я тогда решил, что проблема в экспорте и вообще в том, что я делал с uv mapping. Поиграв пару часов с экспортом, я решил попробовать в blender экспортировать и о чудо, моделька починилась! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WGxvbt9E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/onk85zqb6dfma1msoi44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WGxvbt9E--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/onk85zqb6dfma1msoi44.png" alt="я не знаю что я делаю"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Потратив еще кучу часов в попытке разобраться, в чем же дело. Я заметил, что blender по умолчанию преобразовывал поверхности из 4 точек в поверхности из 3 точек. И когда я отключал данную функцию, то модельки опять ломались. И тогда, я понял, что проблема все это время была в библиотеке webgl-obj-loader. Она ломалась если ей подавали поверхности из 4 точек (на самом деле мне это объяснили в чате).&lt;br&gt;&lt;br&gt;
Я сразу побежал писать &lt;a href="https://github.com/frenchtoast747/webgl-obj-loader/issues/69"&gt;жалобу на проблему&lt;/a&gt;, а потом нашел &lt;a href="https://github.com/frenchtoast747/webgl-obj-loader/pull/66"&gt;pull request&lt;/a&gt; который правил эту багу и прикрепил его к своему issue. &lt;/p&gt;
&lt;h2&gt;
  
  
  Отказ от webgl-obj-loader
&lt;/h2&gt;

&lt;p&gt;Посмотрев на результат мучительной работы, я понял, что это не то, чего я хотел. Линии были толстыми, плюс чем сильней было скругление, тем плотней становилась область. &lt;br&gt;
Еще я понимал, что есть какое-то другое решение, потому что когда я открывал модельку в просмотрщиках моделей, они правильно рисовали результат и красиво прорисовали линии. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LuQgqtsd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5766s5m5xbvlm3x9ogw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LuQgqtsd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o5766s5m5xbvlm3x9ogw.png" alt="сранная либа"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Видя это, я понимал, что все можно рассчитать программно, но не знал как...&lt;br&gt;&lt;br&gt;
И в это время появился рыцарь в сияющих доспехах и спас меня из логова бессилия. Он был тем кто предложил:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Распарси .obj сам и получили нужные значения.  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;На тот момент я не понимал, что вообще это значит и как мне это поможет. И человек накидал в песочнице пример на three.js.  &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/festive-dust-kvumu"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Этот пример был светом. Я сразу же понял, что можно выкидывать &lt;strong&gt;webgl-obj-loader&lt;/strong&gt; и зажить как человек. Выбросил его без каких либо сожалений.&lt;/p&gt;

&lt;p&gt;Продолжение &lt;a href="https://dev.to/dimensi/weggl-3-3k58"&gt;есть&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webgl</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Знакомство фронтендера с WebGL: почему WebGL?</title>
      <dc:creator>Nikita Nafranets</dc:creator>
      <pubDate>Tue, 06 Jul 2021 22:31:11 +0000</pubDate>
      <link>https://dev.to/dimensi/weggl-1-39h5</link>
      <guid>https://dev.to/dimensi/weggl-1-39h5</guid>
      <description>&lt;p&gt;Мне и моему коллеге дизайнеру была поставлена задача разработать новую версию сайта-визитки компании. Коллега полгода учился работать с 3D-редакторами (в нерабочее время на Maxon Cinema 4D), поэтому он хотел использовать свои новые навыки при создании новой версии сайта. Его идея заключалась в том, что на каждой странице на первом экране будет крутиться какая-нибудь непонятная фигура с каким-нибудь красивым текстом. Выглядеть это должно было примерно так:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UPmZOTIj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oq9veban9vt802ju891m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UPmZOTIj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oq9veban9vt802ju891m.jpg" alt="Как должно выглядить"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Реальных моделей пока не было, поэтому на первое время мне предоставили модель яблока.&lt;/p&gt;

&lt;p&gt;Основная проблема заключалась в том, что у меня не было опыта работы с 3D, я очень плохо знал математику и геометрию, а также у меня никогда не было опыта работы с WebGL. В общем, в свои силы я верил слабо. По итогу с задачей я справился, и я хочу рассказать об этом опыте в небольшом цикле статей.&lt;/p&gt;

&lt;h2&gt;
  
  
  Почему вообще WebGL?
&lt;/h2&gt;

&lt;p&gt;Слово WebGL у меня ассоциируется с 3D. Я считаю, что больше нет нормальных способов отрендерить что-то в 3D без WebGL. Помимо того, что слово WebGL само по себе звучит очень круто, нашлись и другие причины выбрать эту технологию:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Мы хотели интерактивности. Например, чтобы моделька реагировала на движение курсора мыши по двум осям. С помощью видео такое сделать невозможно.&lt;/li&gt;
&lt;li&gt;Адаптивность. Мы могли рисовать одну модельку под любые экраны и устройства (поддержка WebGL была подавляющей). Нам не нужно рендерить заранее кучу видюх под разные экраны и переживать о том, что на каком-то экране появится пикселизация.&lt;/li&gt;
&lt;li&gt;Проблемы с iOS. На каждой версии были свои приколы с фоновыми видео на телефоне. На одной из версий видео вообще могло не запуститься из-за политик Apple, а иногда нужно было проводить специальный обряд, чтобы фоновое видео заработало. Таких проблем с WebGL пока нет и, надеюсь, не будет.&lt;/li&gt;
&lt;li&gt;IE11 тоже поддерживает WebGL. Да, есть &lt;a href="https://caniuse.com/#feat=webgl"&gt;нюанс&lt;/a&gt;, но он даже не стоит внимания. &lt;/li&gt;
&lt;li&gt;В SVG невозможно сделать полноценный 3D, только псевдо. Также браузеры плохо себя чувствуют, когда SVG элементов в DOM больше 10 000. Это показали мои попытки отрендерить модельку в SVG.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Я думаю, первого пункта хватит, чтоб принять решение в пользу webGL.&lt;/p&gt;

&lt;h2&gt;
  
  
  С чего начал ?
&lt;/h2&gt;

&lt;p&gt;Модель яблока была в формате &lt;a href="https://en.wikipedia.org/wiki/Wavefront_.obj_file"&gt;OBJ&lt;/a&gt;, другие форматы я решил не рассматривать. Это текстовый формат и это придавало мне какой-то уверенности в том, что решений в интернете для него должно быть много.  &lt;/p&gt;

&lt;p&gt;Я знал про существования библиотек &lt;code&gt;three.js&lt;/code&gt;, &lt;code&gt;Babylon.js&lt;/code&gt; и &lt;code&gt;PixiJS&lt;/code&gt; (это вообще 2D-рендер). Вес 3D-библиотек огромный, как бы они не были сжаты. Не хотелось пускать таких монстров к себе на сайт, у меня и так был 100кб react-dom, куда еще больше? Да и чтоб разобраться в 3D-библиотеках, все равно нужно было иметь какие-то представления о 3D-графике. &lt;br&gt;
Я гуглил «webgl obj model render» и находил только онлайн-просмотрщики или какие-то узко специфичные решения, которые запустить не смог. &lt;br&gt;
Также искал демки на CodePen, но я не нашел ничего подходящего. А если что-то находил, не мог понять, что вообще происходит и что мне делать.&lt;br&gt;
Понял, что нужно начать с основ, без базовых знаний webGL задачу не выполнить.&lt;/p&gt;
&lt;h2&gt;
  
  
  Погружение в WebGL
&lt;/h2&gt;

&lt;p&gt;Не знаю как так получилось, но в интернете на глаза ресурсы по WebGL мне не попадались, поэтому я пошел &lt;a href="https://t.me/webgl_ru"&gt;Телеграм-чат @webgl_ru&lt;/a&gt; (найти его было просто) и спросил: &lt;br&gt;
— как начать в WebGL фронту?&lt;/p&gt;

&lt;p&gt;Похоже, что в чат постоянно заходили подобные мне ребята с аналогичными вопросами, поэтому у ребят из чата уже был заготовлен &lt;a href="https://telegra.ph/Poleznye-resursy-11-08"&gt;список ресурсов&lt;/a&gt;, который они мне и скинули. Впоследствии участники данного чата мне еще не раз помогли, за что им огромное спасибо.&lt;/p&gt;

&lt;p&gt;Из списка, который мне скинули, я выбрал ресурс &lt;a href="https://webglfundamentals.org/"&gt;WebGL Fundamentals&lt;/a&gt;, у которого было довольно говорящее название, а также перевод на русский. Обычно я не вижу ничего ужасного в английской документации, но WebGL казался мне чем-то инородным и страшным, а также состоящим из подходов, которые доселе не были мне знакомы. То, что WebGL рендерит это всё через Canvas — это было единственное, что я знал об этой технологии.&lt;/p&gt;
&lt;h3&gt;
  
  
  Что вообще такое WebGL
&lt;/h3&gt;

&lt;p&gt;Первое, что бросается в глаза, — это необычный API. Привычное нам браузерное API — это просто вызов методов у каких-то встроенных объектов/классов, тогда как апи WebGL это как будто вы программно настраиваете repl node.js, а потом прокидываете в этот repl строки javascript кода и получаете какой-то результат из этого. &lt;br&gt;
В случаи webgl вы настраиваете внутри браузера обрезанную версию &lt;a href="https://ru.wikipedia.org/wiki/OpenGL"&gt;OpenGL&lt;/a&gt;(библиотека которая заставляет наши видеокарты что-то рисовать) и прокидываете в нее код на языке &lt;a href="https://ru.wikipedia.org/wiki/OpenGL_Shading_Language"&gt;GLSL&lt;/a&gt;. GLSL  обрезанный C подобный язык, погрузится несложно. Как будто пишешь на es3 javascript.&lt;/p&gt;

&lt;p&gt;Если обобщить, то работа на webgl выглядит так:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Получаете доступ к webgl (по сути к openGL, но версия обрезанная и потому этому называется webgl).&lt;/li&gt;
&lt;li&gt;Выставляете какие-нить специальные флаги, которые могут менять работу рендера.&lt;/li&gt;
&lt;li&gt;Пишите программу на GLSL. Сама программа это просто функции которые принимает данные и выплевывает какой-то результат. Данные это точки в координатной системе, а также цвет этой точки. Тип как указать положение div 1х1 через absolute и поставить его центр на высоте 300 пикселей, покрасив в красный.&lt;/li&gt;
&lt;li&gt;В случае 3D нужно еще указать глубину точки, ну это пока не важно.&lt;/li&gt;
&lt;li&gt;Пространство в webgl имеет координаты от -1 до 1, потому, нам нужно преобразовывать координаты разных фигур в координаты от -1 до 1, если они за пределами. ВОТ ОНА МАТЕМАТИКА.&lt;/li&gt;
&lt;li&gt;Пропускаете через апи координаты, цвета и другие параметры.&lt;/li&gt;
&lt;li&gt;PROFIT! Мы получаем 2D/3D картинку.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Шейдеры
&lt;/h3&gt;

&lt;p&gt;Выше я говорил про программы на GLSL, программа всегда состоит из 2 шейдеров. Шейдер есть функция.&lt;br&gt;
Каждая программа состоит из вершинного шейдера (Vertex Shader) и фрагментного шейдера (Fragment Shader).&lt;br&gt;&lt;br&gt;
Вершинный шейдер - позволяет разметить пространство, а фрагментный - закрашивает это пространство. Так работают видеокарты. &lt;br&gt;
Сначала им нужно выставить точки в пространстве, потом соединить эти точки невидимыми линиями, а потом закрасить каждый пиксель внутри получившийся фигуры.&lt;br&gt;
Если приводить пример из жизни, у вас есть стенка 1м х 1м и есть художник по имени Видеокарта. Вот вы и ему говорите:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;— Поставь мне точку на 30 сантиметров от верха и 50 см слева, потом точку в 50х50, потом в 70х80, соедини мне это линиями и закрась получившееся пространство красным.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Сами шейдеры выглядит так:&lt;br&gt;&lt;br&gt;
Вершинный шейдер (Vertex Shader)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// атрибут, который будет получать данные которые мы передали. атрибут === переменная/пропс&lt;/span&gt;
&lt;span class="k"&gt;attribute&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// все шейдеры имеют функцию main&lt;/span&gt;
&lt;span class="c1"&gt;// стандартная тема для компилируемых языков&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// gl_Position - специальная переменная вершинного шейдера,&lt;/span&gt;
  &lt;span class="c1"&gt;// которая отвечает за установку положения&lt;/span&gt;
  &lt;span class="nb"&gt;gl_Position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a_position&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Фрагментный шейдер (Fragment Shader)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight glsl"&gt;&lt;code&gt;&lt;span class="c1"&gt;// фрагментные шейдеры не имеют точности по умолчанию, поэтому нам необходимо её&lt;/span&gt;
&lt;span class="c1"&gt;// указать. mediump подойдет для большинства случаев. Он означает "средняя точность"&lt;/span&gt;
&lt;span class="k"&gt;precision&lt;/span&gt; &lt;span class="kt"&gt;mediump&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// gl_FragColor - специальная переменная фрагментного шейдера.&lt;/span&gt;
  &lt;span class="c1"&gt;// Она отвечает за установку цвета.&lt;/span&gt;
  &lt;span class="nb"&gt;gl_FragColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;vec4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// вернёт красный&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В вершинном шейдере вы увидели &lt;code&gt;attribute&lt;/code&gt;. Шейдере есть несколько типов переменных (дальше копипаста с webglfundamentals.org):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Атрибуты и буферы&lt;br&gt;&lt;br&gt;
Буферы - это массивы бинарных данных, загруженных в графический процессор. Обычно буферы содержат вещи вроде положений вершин, нормалей, координат текстур, цветов вершин и т.д., хотя вы вольны положить в них что угодно.&lt;br&gt;&lt;br&gt;
Атрибуты определяют, каким образом данные из ваших буферов передаются в вершинный шейдер. Например, вы можете поместить положения вершин в буфер как три 32-битных числа с плавающей точкой на одно положение. Вы указываете конкретному атрибуту, откуда брать положения вершин, какой тип данных используется (три 32-битных числа с плавающей точкой), начиная с какого индекса в буфере начинаются положения вершин и какое количество байтов нужно получить от одного положения до следующего.&lt;br&gt;&lt;br&gt;
Доступ к буферам не произвольный. Вместо этого вершинный шейдер выполняется заданное количество раз и каждый раз, когда он выполняется, выбирается следующее значение каждого из указанных буферов и назначается атрибуту.&lt;/p&gt;

&lt;p&gt;Uniform-переменные&lt;br&gt;&lt;br&gt;
Uniform-переменные - это глобальные переменные, которые устанавливаются перед выполнением программы шейдера.&lt;/p&gt;

&lt;p&gt;Текстуры&lt;br&gt;&lt;br&gt;
Текстуры - это массивы данных, к которым есть произвольный доступ в программе шейдера. Чаще всего в текстуру помещается картинка, но текстура - это просто набор данных и вы можете запросто поместить в неё что-то отличное от набора цветов.&lt;/p&gt;

&lt;p&gt;constying-переменные&lt;br&gt;&lt;br&gt;
constying-переменные позволяют передавать данные из вершинного шейдера фрагментному шейдеру. Во фрагментном шейдере мы получим интерполированные значения вершинного шейдера - зависит от того, отображаем ли мы точки, линии или треугольники.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Вершинный шейдер выполняется на каждую порцию x,y,z (z может не указываться, если рисуем в 2D) координат. Каждые такие координаты создают вершину. А дальше уже эти вершины объединяются в треугольники (полигоны) и потом фрагментным шейдером эти треугольники закрашиваются.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Вы спросите, почему именно треугольники?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;В процессе обучения я на это не обращал внимания, но когда начал предпринимать попытки нарисовать модельку тоже удивился, но оказывается любую фигуру можно нарисовать через ТРЕУГОЛЬНИКИ (ПОЛИГОНЫ) и потому, добавлять другие фигуры бессмысленно.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Треугольник есть абсолют&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
В webgl можно рисовать только треугольниками, линиями и точками.  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Если рисовать через линии, то будут только отрисовываться грани между вершинами, но все что внутри фигуры не будет закрашиваться.&lt;/li&gt;
&lt;li&gt;Если рисовать через точки, то будут отрисовываться только точки! Удивительно!&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Матрицы
&lt;/h3&gt;

&lt;p&gt;Также я узнал про матрицы. Это пришло из математики и для js разраба это выглядит как массив из 9 или 12 чисел (12 для 3D). &lt;br&gt;
Матрицы решают вопросы того как трансформировать модель (а точней вершины), чтоб поставить модельку в нужное место в пространстве, увеличить или покрутить. &lt;br&gt;
Матрицы еще позволяют создавать камеры, то есть менять вид обзора и другое. Вы могли с ними встречаться, если работали &lt;code&gt;transform: matrix(...n)&lt;/code&gt; в css.&lt;br&gt;&lt;br&gt;
Матрицы один из &lt;strong&gt;фундаментов 2D/3D графики&lt;/strong&gt;. Наверно одна из немногих вещей которой можно пользоваться не разбираясь как она работает.&lt;br&gt;&lt;br&gt;
Достаточно запомнить, что чтоб применить несколько трансформаций нужно просто матрицы друг на друга перемножить и результат передать в шейдер. &lt;br&gt;
Матрица 3х3 для 2D трансформаций, а 4х4 для 3D трансформаций.&lt;br&gt;&lt;br&gt;
Хорошие люди уже все за нас написали &lt;a href="http://glmatrix.net/"&gt;gl-matrix&lt;/a&gt;. Нам нужно вызывать только знакомые на слух название методов и получать нужный результат. &lt;br&gt;
Более подробно про матрицы можно узнать на &lt;a href="https://webglfundamentals.org/webgl/lessons/ru/webgl-2d-matrices.html"&gt;webgl fundumentals&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Hello world на webgl
&lt;/h3&gt;

&lt;p&gt;Так как все же выглядит hello world код на webgl? Что вообще требуется для того, чтоб это запустить и нарисовать треугольник?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Нужно получить ссылку на canvas элемент.&lt;/li&gt;
&lt;li&gt;Получить из него webgl контекст, то есть то что нам позволит общаться с webgl и рисовать через него.&lt;/li&gt;
&lt;li&gt;Создать программу из вершинного шейдера и фрагментного шейдера.&lt;/li&gt;
&lt;li&gt;Получить ссылки на переменные из шейдеров.&lt;/li&gt;
&lt;li&gt;Присвоить переменным - данные.&lt;/li&gt;
&lt;li&gt;Запустить функцию drawArrays у webgl.&lt;/li&gt;
&lt;li&gt;Вуаля, мы получили наш треугольник.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;И после такого количества кода (по ссылке), мы получаем треугольник.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/dimensi/embed/GRJrgjy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Честно говоря, это безумное количества кода ради одного треугольника немного остудило желание, но автор учебника объяснил, что все это можно убрать под хелперы. &lt;br&gt;
Посмотрев на этот пример, можно понять безумные размеры 3D либ.&lt;/p&gt;

&lt;p&gt;Продолжение &lt;a href="https://dev.to/dimensi/weggl-2-3o0m"&gt;здесь&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webgl</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
