<?xml version="1.0" encoding="utf-8" ?><rss version="2.0" xmlns:tt="http://teletype.in/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Olga Khusainova</title><generator>teletype.in</generator><description><![CDATA[Olga Khusainova]]></description><image><url>https://img2.teletype.in/files/15/92/159261b6-e151-4e5f-a7b8-d57aa481aada.png</url><title>Olga Khusainova</title><link>https://teletype.in/@khusainova</link></image><link>https://teletype.in/@khusainova?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova</link><atom:link rel="self" type="application/rss+xml" href="https://teletype.in/rss/khusainova?offset=0"></atom:link><atom:link rel="next" type="application/rss+xml" href="https://teletype.in/rss/khusainova?offset=10"></atom:link><atom:link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></atom:link><pubDate>Wed, 24 Jun 2026 14:16:05 GMT</pubDate><lastBuildDate>Wed, 24 Jun 2026 14:16:05 GMT</lastBuildDate><item><guid isPermaLink="true">https://teletype.in/@khusainova/qrfN2NsIPJT</guid><link>https://teletype.in/@khusainova/qrfN2NsIPJT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova</link><comments>https://teletype.in/@khusainova/qrfN2NsIPJT?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova#comments</comments><dc:creator>khusainova</dc:creator><title>RGBM-кодировка цвета</title><pubDate>Wed, 15 Mar 2023 19:25:34 GMT</pubDate><description><![CDATA[<img src="https://img1.teletype.in/files/4d/10/4d108452-5518-4066-bfa9-1de51d165147.png"></img>Хочется хранить цвета в вершинах как byte4, но значения у них могут быть сильно выше единицы. Следовательно, нужно как-то нормализовать.
Можно просто поделить на условную максимальную яркость и затем умножить в шейдере.
А можно использовать более хитрую кодировку в rgbm. Преимущество rgbm в том, что чем меньше значения цвета, тем меньше разница между оригиналом и декодированным значением, ошибка растет пропорционально яркости.]]></description><content:encoded><![CDATA[
  <p id="GM89">Хочется хранить цвета в вершинах как byte4, но значения у них могут быть сильно выше единицы. Следовательно, нужно как-то нормализовать.<br />Можно просто поделить на условную максимальную яркость и затем умножить в шейдере.<br />А можно использовать более хитрую кодировку в rgbm. Преимущество rgbm в том, что чем меньше значения цвета, тем меньше разница между оригиналом и декодированным значением, ошибка растет пропорционально яркости.</p>
  <figure id="LXCg" class="m_original">
    <img src="https://img1.teletype.in/files/4d/10/4d108452-5518-4066-bfa9-1de51d165147.png" width="604" />
  </figure>
  <figure id="eVx4" class="m_original">
    <img src="https://img3.teletype.in/files/a2/a6/a2a6d014-710f-4de0-ad23-292dd8535efd.png" width="606" />
  </figure>
  <p id="SnUv">Кодировка цвета в скрипте</p>
  <pre id="Iwlf">private static float MAX_BRIGHTNESS = 60;
private static Byte4 ColorToRGBM(Color color) {   
    float y = color.maxColorComponent;   
    y = math.clamp(math.ceil(y * 255 / MAX_BRIGHTNESS), 1, 255);   
    color *= 255 * 255 / (y * MAX_BRIGHTNESS);   
    Byte4 i = new Byte4((byte)color.r, (byte)color.g, (byte)color.b, (byte)y);   
    return i;
}</pre>
  <p id="OkWn"></p>
  <p id="OD56">Декодировка в шейдере:</p>
  <pre id="5Ls2">#define MAX_BRIGHTNESS 60
half3 DecodeColor(half4 data) {        
    return data.rgb * data.a * MAX_BRIGHTNESS;
}</pre>
  <p id="kboQ" data-align="right"><a href="https://t.me/lab_sborki" target="_blank">https://t.me/lab_sborki</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@khusainova/RLXTMHMuH3j</guid><link>https://teletype.in/@khusainova/RLXTMHMuH3j?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova</link><comments>https://teletype.in/@khusainova/RLXTMHMuH3j?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova#comments</comments><dc:creator>khusainova</dc:creator><title>Blizzard BlendAdd shading</title><pubDate>Fri, 02 Sep 2022 13:13:24 GMT</pubDate><description><![CDATA[<img src="https://img1.teletype.in/files/83/6b/836b6b61-2942-45a1-8eaf-b3333720546e.png"></img>В этом занимательном видео начиная с 19:45 разработчик из Близзард рассказывает о способе рисовать эффекты так, чтобы они хорошо выглядели и в темном и в светлом окружении, не уходя слишком в белый, но и не теряя свечения. Этот способ они называют BlendAdd. ]]></description><content:encoded><![CDATA[
  <p id="xrdQ">В этом занимательном <a href="https://www.youtube.com/watch?v=YPy2hytwDLM" target="_blank">видео </a>начиная с 19:45 разработчик из Близзард рассказывает о способе рисовать эффекты так, чтобы они хорошо выглядели и в темном и в светлом окружении, не уходя слишком в белый, но и не теряя свечения. Этот способ они называют <em>BlendAdd</em>. </p>
  <p id="XyOl">Идея состоит в том, что  в <em>rgb </em>мы храним изображение эффекта на черном фоне, а альфа-канал определяет насколько аддитивно, а насколько через бленд должно накладываться изображение. <em>1</em> - полностью через <em>blend</em>, <em>0</em> - полностью через <em>add</em>. </p>
  <p id="tuGC">Но как же это конкретно реализовать?</p>
  <p id="WMz6">Запишем выражение и раскроем скобочки:</p>
  <figure id="JWhZ" class="m_original">
    <img src="https://img1.teletype.in/files/83/6b/836b6b61-2942-45a1-8eaf-b3333720546e.png" width="649" />
  </figure>
  <p id="pk6V">Осталось воплотить эту формулу через параметры бленда и операции в шейдере. К <em>dst </em>тут явно просится <em>OneMinusSrcAlpha</em>, но для соответствия в альфа-канал фрагмента надо записать квадрат альфы. A для <em>src </em>установим <em>One </em>и прямо в шейдере домножим на <em>(1 - alpha + alpha^2)</em></p>
  <pre id="lLfN">Shader &quot;EffectBlendAdd&quot; {
   ...
   SubShader {
       ...
       Blend One OneMinusSrcAlpha
       ...
       
       Pass {   
           HLSLPROGRAM
           ...
           half4 Fragment() {
              ...
               output.rgb *= 1 - a + a * a;
               output.a = a * a;
               return outputColor;
           }
           ENDHLSL
       }
           
   }
}</pre>
  <p id="A6OV"></p>
  <p id="Qpjb" data-align="right"><a href="https://t.me/lab_sborki" target="_blank">https://t.me/lab_sborki</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@khusainova/dTgCDEpJ89R</guid><link>https://teletype.in/@khusainova/dTgCDEpJ89R?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova</link><comments>https://teletype.in/@khusainova/dTgCDEpJ89R?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova#comments</comments><dc:creator>khusainova</dc:creator><title>Генерация случайных точек на поверхности мешей с помощью линий</title><pubDate>Fri, 26 Aug 2022 12:17:24 GMT</pubDate><media:content medium="image" url="https://img3.teletype.in/files/ac/82/ac82b98a-aa61-4693-baea-78c4930de1a9.png"></media:content><description><![CDATA[<img src="https://img3.teletype.in/files/64/82/6482b55d-1d7c-4071-bdec-3f952e6072e4.png"></img>В этом посте речь пойдет о способе покрыть случайными равномерно-распределенными точками только видимые поверхности сцены, исключая поверхности внутри пересечений мешей.  ]]></description><content:encoded><![CDATA[
  <p id="FG26">В этом посте речь пойдет о способе покрыть случайными равномерно-распределенными точками только видимые поверхности сцены, исключая поверхности внутри пересечений мешей.  </p>
  <p id="WzWt">Метод можно погуглить как &quot;Sampling with Global Lines&quot; или &quot;Sampling with Uniformly Distributed Lines&quot;. Есть несколько интересных вариаций, расскажу о самой простой.</p>
  <p id="k7sl">Итак:</p>
  <ol id="RVDj">
    <li id="bhYt">Формируем баунд-сферу вокруг геометрии сцены.</li>
    <li id="8UNT">Берем две случайные точки на поверхности баунд-сферы и делаем из них линию.</li>
    <li id="U9O4">Находим точки пересечения линии с геометрией уровня. Это и будут наши искомые точки на поверхности.  </li>
    <li id="0zkg">Повторяем 2-4.</li>
  </ol>
  <figure id="BSLH" class="m_custom">
    <img src="https://img3.teletype.in/files/64/82/6482b55d-1d7c-4071-bdec-3f952e6072e4.png" width="543.1193181818182" />
  </figure>
  <p id="VWXm">Если все меши на сцене замкнутые, то  исключить точки внутри пересечений мешей довольно легко, подсчитывая число входов и выходов луча в поверхность по ходу рейкаста.</p>
  <figure id="5164" class="m_custom">
    <img src="https://img4.teletype.in/files/35/0d/350d1024-cc4c-4c1c-8b86-a10286b9251d.png" width="542.0359712230216" />
  </figure>
  <p id="oWVu">Результат такой же, как и в <a href="https://teletype.in/@khusainova/duhWjKHJJv4" target="_blank">методе inverse cdf</a>,  с той лишь разницей, что мы исключили невидимые точки</p>
  <figure id="wUbl" class="m_original">
    <img src="https://img2.teletype.in/files/19/77/19774633-11b7-40a7-a49b-0d4c365665e1.png" width="549" />
  </figure>
  <p id="OjpL" data-align="right"><a href="https://t.me/lab_sborki" target="_blank">https://t.me/lab_sborki</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@khusainova/duhWjKHJJv4</guid><link>https://teletype.in/@khusainova/duhWjKHJJv4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova</link><comments>https://teletype.in/@khusainova/duhWjKHJJv4?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova#comments</comments><dc:creator>khusainova</dc:creator><title>Генерация случайных точек на поверхности меша</title><pubDate>Mon, 20 Jun 2022 10:21:02 GMT</pubDate><media:content medium="image" url="https://img1.teletype.in/files/8e/91/8e912a5d-c959-4f66-ad61-903fef824912.png"></media:content><description><![CDATA[<img src="https://img2.teletype.in/files/d6/3d/d63de89b-738e-4cb4-82ad-4b763d4773a3.png"></img>Задача: сгенерировать N равномерно распределенных точек по поверхности произвольного триангулированного меша.]]></description><content:encoded><![CDATA[
  <p id="2x2C">Задача: сгенерировать N равномерно распределенных точек по поверхности произвольного триангулированного меша.</p>
  <figure id="4phF" class="m_custom">
    <img src="https://img2.teletype.in/files/d6/3d/d63de89b-738e-4cb4-82ad-4b763d4773a3.png" width="426.712962962963" />
  </figure>
  <p id="dRxr">Общая идея решения: выбираем треугольник с вероятностью, пропорциональной его площади, и уже внутри выбранного треугольника берем равномерно распределенную случайную точку. </p>
  <h3 id="8ybq">Как выбрать треугольник с вероятностью, пропорциональной его площади? </h3>
  <p id="Eqbi">Воспользуемся методом, который называется inverse cdf sampling. Подробно можно почитать <a href="https://www.pbr-book.org/3ed-2018/Monte_Carlo_Integration/Sampling_Random_Variables" target="_blank">здесь.</a></p>
  <p id="obK5">Представим, что у нас есть случайная величина и она может принимать 4 возможных дискретных значения  <em>v1, v2, v3 </em>и<em> v4</em> с вероятностями <em>p1, p2, p3 </em>и<em> p4 </em>соответственно.  </p>
  <p id="xPBv" data-align="center"><em>p1 + p2 + p3 + p4 = 1</em></p>
  <p id="IugK">Сконструируем дискретную функцию cdf, где<em> cdf[i] = p0 + p1 + ... + pi. </em></p>
  <figure id="Yqdr" class="m_custom">
    <img src="https://img4.teletype.in/files/39/2c/392c9be4-e36b-4543-a918-2dacd9a9524f.png" width="769.2035398230089" />
  </figure>
  <p id="we4y">И теперь используя cdf мы можем замапить равномерно распределенную случайную величину на наши дискретные случайные величины<em> v0, v1, v2, v3</em> с соответствующими вероятностями</p>
  <pre id="bxu0">/// &lt;summary&gt; Inverse cdf sampling &lt;/summary&gt;
/// &lt;param name=&quot;cumulativeProbabilities&quot;&gt;дискретная cdf. 
/// cumulativeProbabilities[0] - p0, где p0 - вероятность, что выпадет случайная величина с индексом 0 
/// cumulativeProbabilities[1] - p0 + p1, где p1 - вероятность, что выпадет случайная величина с индексом 1 
/// cumulativeProbabilities[2] - p0 + p1 + p2, где p2 - вероятность, что выпадет случайная величина с индексом 2 
/// ...
/// cumulativeProbabilities[cumulativeProbabilities.Length - 1] = p0 + p1 + p2 + ... + pLast = 1&lt;/param&gt;
/// &lt;param name=&quot;randomValue&quot;&gt;равномерно распределенная случайная величина&lt;/param&gt;
/// &lt;returns&gt;индекс выпавшей случайной величины&lt;/returns&gt;
public static int InverseSample(float[] cumulativeProbabilities, float randomValue) {
   int maxIndex = cumulativeProbabilities.Length - 1;   
   int minIndex = 0;   
      
   for (int i = 0; i &lt; cumulativeProbabilities.Length; i++) {      
       int index = minIndex + (int)(0.5f * (maxIndex - minIndex));      
       var pr = cumulativeProbabilities[index];      
       if (randomValue &lt;= pr) {         
           maxIndex = index;      
       } else {         
           minIndex = index + 1;      
       }      
       if (minIndex == maxIndex) return maxIndex;             
   }
   
   throw new Exception(&quot;Something was wrong&quot;);
}</pre>
  <p id="rJjp"></p>
  <p id="6ELK">Теперь остается сконструировать <em>cumulativeProbabilities </em>для наших треугольников и, используя обычный <em>random </em>и метод <em>InverseSample</em>, получать индексы треугольников с вероятностью, пропорциональной их площади.</p>
  <pre id="JWkO">float totalArea = 0;
for (var i = 0; i &lt; triangles.Length; i++)
{    
    var triangle = triangles[i];    
    var triangleArea = CalculateTriangleArea(triangle);
    totalArea += triangleArea;
}  
var cumulativeProbabilities = new float[triangles.Length];
float probabilitySum = 0;
for (var i = 0; i &lt; triangles.Length; i++)
{    
    var triangle = triangles[i];    
    var triangleArea = CalculateTriangleArea(triangle);
    var probability = triangleArea /totalArea;
    probabilitySum += probability;
    cumulativeProbabilities[i] = probabilitySum;    
}  
var triangleRandom = new Random(seed);
for (int i = 0; i &lt; N; i++) {
    var index = InverseSample(cumulativeProbabilities, triangleRandom.NextFloat());
    var trianlge = triangles[index];
    ...
}
         </pre>
  <h3 id="W04a"></h3>
  <h3 id="Nsyy">Как сгенерировать равномерно распределенную случайную точку внутри треугольника</h3>
  <pre id="m3u5">float3 v0 = triangle.v0; // 1я вершина
float3 v1 = triangle.v1; // 2я вершина
float3 v2 = triangle.v2; // 3я вершина

var random = new Random(seed);
var r1 = random.NextValue();
var r2 = random.NextValue();
float sqrtR1 = math.sqrt(r1);
// Получим барицентрические координаты
w0 = 1 - sqrtR1;
w1 = (1 - r2) * sqrtR1;
w2 = r2 * sqrtR1;

var randomPoint = w0*v0 + w1*v1 + w2*v2; //Результат</pre>
  <p id="A7R9"></p>
  <p id="8yJM" data-align="right"><a href="https://t.me/lab_sborki" target="_blank">https://t.me/lab_sborki</a></p>

]]></content:encoded></item><item><guid isPermaLink="true">https://teletype.in/@khusainova/OSs7zrhdxsH</guid><link>https://teletype.in/@khusainova/OSs7zrhdxsH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova</link><comments>https://teletype.in/@khusainova/OSs7zrhdxsH?utm_source=teletype&amp;utm_medium=feed_rss&amp;utm_campaign=khusainova#comments</comments><dc:creator>khusainova</dc:creator><title>Трансформация нормалей</title><pubDate>Thu, 26 May 2022 12:29:55 GMT</pubDate><description><![CDATA[Для трансформации нормалей на объекте с отрицательным скейлом метод Unity transform.TransformDirection()  дает некорректный результат. Вместо него нужно использовать более дорогой, но правильный код]]></description><content:encoded><![CDATA[
  <p id="ssG2">Для трансформации нормалей на объекте с отрицательным скейлом метод Unity transform.TransformDirection()  дает некорректный результат. Вместо него нужно использовать более дорогой, но правильный код</p>
  <pre id="s7L1">public static Vector3 TransformDir(Transform transform, Vector3 dir){
    var matrix = transform.localToWorldMatrix;    
    var matrixInverse = matrix.inverse;    
    var matrixTranspose = matrixInverse.transpose;    
    var n = (Vector3) (matrixTranspose * dir);    
    return n;
}</pre>
  <p id="0Gm7">Подробное объяснение в главе 4.5 книги<a href="https://www.mathfor3dgameprogramming.com/" target="_blank"> Mathematics for 3D Game Programming and Computer Graphics</a></p>
  <p id="7O7N"></p>
  <p id="qJkL"></p>
  <p id="3sY5" data-align="right"><a href="https://t.me/lab_sborki" target="_blank">https://t.me/lab_sborki</a></p>

]]></content:encoded></item></channel></rss>