<?xml version="1.0" encoding="utf-8" ?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:tt="http://teletype.in/" xmlns:opensearch="http://a9.com/-/spec/opensearch/1.1/"><title>𝚁𝚞𝚜𝚕𝚊𝚗 𝙶𝚘𝚕𝚘𝚟𝚒𝚗𝚜𝚔𝚢</title><subtitle>I'm a C++ Game &quot;Not-Engine-Yet&quot; Developer</subtitle><author><name>𝚁𝚞𝚜𝚕𝚊𝚗 𝙶𝚘𝚕𝚘𝚟𝚒𝚗𝚜𝚔𝚢</name></author><id>https://teletype.in/atom/golxzn</id><link rel="self" type="application/atom+xml" href="https://teletype.in/atom/golxzn?offset=0"></link><link rel="alternate" type="text/html" href="https://teletype.in/@golxzn?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=golxzn"></link><link rel="next" type="application/rss+xml" href="https://teletype.in/atom/golxzn?offset=10"></link><link rel="search" type="application/opensearchdescription+xml" title="Teletype" href="https://teletype.in/opensearch.xml"></link><updated>2026-05-14T00:56:56.812Z</updated><entry><id>golxzn:base-phong-light-summary</id><link rel="alternate" type="text/html" href="https://teletype.in/@golxzn/base-phong-light-summary?utm_source=teletype&amp;utm_medium=feed_atom&amp;utm_campaign=golxzn"></link><title>The &quot;Phong Light&quot; Summary</title><published>2024-09-24T03:17:12.958Z</published><updated>2024-09-24T03:17:12.958Z</updated><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img2.teletype.in/files/59/f6/59f60f48-ef7e-4409-8ac8-af8efad5d345.png"></media:thumbnail><category term="open-gl" label="OpenGL"></category><summary type="html">&lt;img src=&quot;https://img1.teletype.in/files/8a/a5/8aa554ab-50dd-4122-89f2-0b073fc7d000.png&quot;&gt;The lighting is hell. For me, personally, it's pain to make the lighting shader. I mean, not just a simple ambient light everywhere. Whenever I tried to make something a little fucking bit complex, like provide multiple lighting and combine them with the object material, everything fucks up.</summary><content type="html">
  &lt;nav&gt;
    &lt;ul&gt;
      &lt;li class=&quot;m_level_1&quot;&gt;&lt;a href=&quot;#wRcb&quot;&gt;Preface&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_1&quot;&gt;&lt;a href=&quot;#L6mV&quot;&gt;Let's talk about the data&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#59sC&quot;&gt;Vertex data&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#Pa4C&quot;&gt;Model or Model View?&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#fDmU&quot;&gt;Don't forget about normal!&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#5Lsd&quot;&gt;Normal Matrix Calculation&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#1DQA&quot;&gt;Normal matrix with ModelView transform&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#tEaM&quot;&gt;Final possible vertex shaders&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_1&quot;&gt;&lt;a href=&quot;#q6zw&quot;&gt;Base model and its modifications&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_1&quot;&gt;&lt;a href=&quot;#G0Xu&quot;&gt;The Light Components&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#kfll&quot;&gt;Ambient Lie&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#BQTJ&quot;&gt;Diffuse Lie&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#mzvK&quot;&gt;Specular Lie&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_2&quot;&gt;&lt;a href=&quot;#2HR8&quot;&gt;Combination of Lies&lt;/a&gt;&lt;/li&gt;
      &lt;li class=&quot;m_level_1&quot;&gt;&lt;a href=&quot;#VALH&quot;&gt;What's next?&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/nav&gt;
  &lt;h2 id=&quot;wRcb&quot;&gt;Preface&lt;/h2&gt;
  &lt;p id=&quot;5mh4&quot;&gt;The lighting is hell. For me, personally, it&amp;#x27;s pain to make the lighting shader. I mean, not just a simple ambient light everywhere. Whenever I tried to make something a little fucking bit complex, like provide multiple lighting and combine them with the object material, everything fucks up.&lt;/p&gt;
  &lt;p id=&quot;ldOn&quot;&gt;I knew what&amp;#x27;s the main problem, but I always defer this shit because &amp;quot;it&amp;#x27;s not time to learn this complex stuff&amp;quot;. Yes. The main issue was my weak understanding of Phong Light theory.&lt;/p&gt;
  &lt;p id=&quot;fUq1&quot;&gt;And, after a hard week investigation of this shit, I finally could say that I understand something good enough.&lt;/p&gt;
  &lt;p id=&quot;G7ET&quot;&gt;But I&amp;#x27;ll definitely forget it, so this is the reason of this summary.&lt;/p&gt;
  &lt;hr /&gt;
  &lt;h2 id=&quot;L6mV&quot;&gt;Let&amp;#x27;s talk about the data&lt;/h2&gt;
  &lt;p id=&quot;DbHl&quot;&gt;The data is everything. And the Graphics Programming obeys this law as well.&lt;/p&gt;
  &lt;p id=&quot;NTH0&quot;&gt;And the lighting shaders could be really different depends on the vertex input data, so let&amp;#x27;s take a look at the most popular data layouts! &lt;/p&gt;
  &lt;h3 id=&quot;59sC&quot;&gt;Vertex data&lt;/h3&gt;
  &lt;p id=&quot;K9wS&quot;&gt;The vertex attributes are essential and commonly contains of vertex position, the normal, and texture coordinates:&lt;/p&gt;
  &lt;pre id=&quot;nn5U&quot; data-lang=&quot;c&quot;&gt;layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
layout(location = 2) in vec2 a_uv;&lt;/pre&gt;
  &lt;p id=&quot;IBLl&quot;&gt;Nothing really special. The &lt;a href=&quot;https://learnopengl.com/Getting-started/Shaders&quot; target=&quot;_blank&quot;&gt;LearnOpenGL&lt;/a&gt; describes it perfectly. So, what could be wrong? &lt;/p&gt;
  &lt;h3 id=&quot;Pa4C&quot;&gt;Model or Model View?&lt;/h3&gt;
  &lt;p id=&quot;tH1n&quot;&gt;When we&amp;#x27;re done with our attributes, we really need to provide the transform matrices:&lt;/p&gt;
  &lt;ol id=&quot;Hg6c&quot;&gt;
    &lt;li id=&quot;hAOb&quot;&gt;&lt;code&gt;Model&lt;/code&gt; — Usually the game object transformation;&lt;/li&gt;
    &lt;li id=&quot;jWsf&quot;&gt;&lt;code&gt;View&lt;/code&gt; — The camera transformation;&lt;/li&gt;
    &lt;li id=&quot;tnMg&quot;&gt;&lt;code&gt;Projection&lt;/code&gt; — describes the viewport transformation.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;UEPZ&quot;&gt;And we know why and how they&amp;#x27;re used:&lt;/p&gt;
  &lt;pre id=&quot;oaym&quot; data-lang=&quot;c&quot;&gt;uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;

void main() {
    gl_Position = u_projection * u_view * u_model * vec4(a_position, 1.0);
}&lt;/pre&gt;
  &lt;p id=&quot;uwe4&quot;&gt;But sometimes you could meet another version of this code:&lt;/p&gt;
  &lt;pre id=&quot;DANS&quot; data-lang=&quot;c&quot;&gt;uniform mat4 u_model_view;
uniform mat4 u_projection;

void main() {
    gl_Position = u_projection * u_model_view * vec4(a_position, 1.0);
}&lt;/pre&gt;
  &lt;p id=&quot;DV9K&quot;&gt;And there are 2 reasons of passing already multiplied model and view matrices:&lt;/p&gt;
  &lt;ol id=&quot;1m7Q&quot;&gt;
    &lt;li id=&quot;9DFG&quot;&gt;The ancient OpenGL versions with the fixed pipeline used to do so;&lt;/li&gt;
    &lt;li id=&quot;9AbY&quot;&gt;The optimization;&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;Mdco&quot;&gt;And that&amp;#x27;s reasonable. You could have a model with thousand of vertices, and the whole scene could have up to millions of vertices, so &lt;em&gt;it&amp;#x27;s expensive to multiply matrixes inside the shaders.&lt;/em&gt; And using SIMD instructions (like in the &lt;a href=&quot;https://github.com/g-truc/glm&quot; target=&quot;_blank&quot;&gt;glm&lt;/a&gt; library) you could find this multiplication once before passing to the shader.&lt;/p&gt;
  &lt;p id=&quot;4FbR&quot;&gt;Those 2 versions changes how we calculate the lighting as well, and we&amp;#x27;ll see it.&lt;/p&gt;
  &lt;h3 id=&quot;fDmU&quot;&gt;Don&amp;#x27;t forget about normal!&lt;/h3&gt;
  &lt;p id=&quot;a0kS&quot;&gt;If you read the LearnOpenGL really pedantically, my congratulations! But I didn&amp;#x27;t get it for 2 years 💀. &lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;SOnG&quot; data-align=&quot;center&quot;&gt;The &lt;code&gt;a_normal&lt;/code&gt; has to be transformed too!&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;KnlN&quot;&gt;But we cannot just multiply the &lt;code&gt;u_model&lt;/code&gt; to the &lt;code&gt;a_normal&lt;/code&gt;, because the shear and scale will affect them as well:&lt;/p&gt;
  &lt;figure id=&quot;m8Z9&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/1d/e9/1de929d5-bc99-402c-945d-e9f7c7c0bd59.png&quot; width=&quot;762&quot; /&gt;
    &lt;figcaption&gt;The u_model * a_normal visualization and some math&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;qPz3&quot;&gt;As you could see in the image above, the multiplication makes the normals useless since they&amp;#x27;re no perpendicular to the face anymore. And to fix it, we need to make such transformation matrix, which transforms the normals better way.&lt;/p&gt;
  &lt;p id=&quot;IGRD&quot;&gt;The best visualization of this problem I&amp;#x27;ve found in the &lt;a href=&quot;https://webglfundamentals.org/webgl/lessons/webgl-3d-lighting-directional.html&quot; target=&quot;_blank&quot;&gt;webglfundamentals.org&lt;/a&gt;, and the best math explanation is in the &lt;a href=&quot;https://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix/&quot; target=&quot;_blank&quot;&gt;lighthouse3d.&lt;/a&gt;&lt;/p&gt;
  &lt;p id=&quot;279X&quot;&gt;You could check them and figure out how it works, but I&amp;#x27;ll write some thoughts here for myself. You could skip it as I&amp;#x27;d been doing till this week...&lt;/p&gt;
  &lt;h3 id=&quot;5Lsd&quot;&gt;Normal Matrix Calculation&lt;/h3&gt;
  &lt;p id=&quot;lXnI&quot;&gt;&lt;/p&gt;
  &lt;ol id=&quot;yZ5j&quot;&gt;
    &lt;li id=&quot;Y7QN&quot;&gt;The normal is always perpendicular to the face, and that&amp;#x27;s mean that the angle between them is 90 degrees or cosines between normalized vectors of them is &lt;code&gt;0&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;JbCz&quot;&gt;Knowing this shit, we could guess that the &lt;strong&gt;dot product&lt;/strong&gt; of &lt;strong&gt;&lt;em&gt;normal&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;a&lt;/em&gt;&lt;/strong&gt; face vectors is &lt;code&gt;0&lt;/code&gt; as well;&lt;/li&gt;
    &lt;li id=&quot;bUPI&quot;&gt;So, let guess that &lt;code&gt;X&lt;/code&gt; is such a transform, which gives us the &lt;strong&gt;dot product&lt;/strong&gt; between transformed &lt;strong&gt;&lt;em&gt;normal&lt;/em&gt;&lt;/strong&gt; and transformed &lt;strong&gt;&lt;em&gt;surface vector &amp;quot;a&amp;quot;&lt;/em&gt;&lt;/strong&gt; same &lt;code&gt;0&lt;/code&gt;: &lt;br /&gt;&lt;code&gt;dot(X * a_normal, u_model * a) == 0&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;uS39&quot;&gt;So, we could replace the &lt;strong&gt;dot production&lt;/strong&gt; with just production of vectors by transposing the left part: &lt;code&gt;transpose(X * a_normal) * u_model * a == 0&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;EWya&quot;&gt;There&amp;#x27;s only one way to get &lt;code&gt;0&lt;/code&gt; here. If the multiplication of &lt;code&gt;transpose(x)&lt;/code&gt; and &lt;code&gt;u_model&lt;/code&gt; gives us the &lt;em&gt;identity matrix&lt;/em&gt;: &lt;code&gt;transpose(X) * u_normal == I&lt;/code&gt;;&lt;/li&gt;
    &lt;li id=&quot;3z9f&quot;&gt;So, the last transformation is getting X from the line above:&lt;br /&gt;&lt;code&gt;X = transpose(inverse(u_normal))&lt;/code&gt;&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;TggA&quot;&gt;Now we can happily calculate this shit on the CPU since it&amp;#x27;s super hard to calculate for each vertex and pass to the shader through &lt;code&gt;u_normal_matrix&lt;/code&gt; uniform:&lt;/p&gt;
  &lt;pre id=&quot;Kz8P&quot;&gt;uniform mat4 u_model;
uniform mat4 u_view;
uniform mat4 u_projection;
uniform mat3 u_normal_uniform;&lt;/pre&gt;
  &lt;p id=&quot;KafB&quot;&gt;&lt;strong&gt;&lt;em&gt;Notice&lt;/em&gt;&lt;/strong&gt; that the &lt;code&gt;u_normal_uniform&lt;/code&gt; is 3x3 matrix. We don&amp;#x27;t really need to have 4 dimensions since the normal isn&amp;#x27;t transform and shouldn&amp;#x27;t be normalized. So, you could make your program faster by converting the model matrix into 3x3 matrix and inverse and transpose it easier!&lt;/p&gt;
  &lt;pre id=&quot;m0tl&quot; data-lang=&quot;cpp&quot;&gt;const mat3x3 normal_uniform{
    transpose(inverse(mat3x3{ object.transform }))
};
pipeline.set_uniform(&amp;quot;u_model&amp;quot;, object.transform);
pipeline.set_uniform(&amp;quot;u_normal_uniform&amp;quot;, normal_uniform);&lt;/pre&gt;
  &lt;h3 id=&quot;1DQA&quot;&gt;Normal matrix with ModelView transform&lt;/h3&gt;
  &lt;p id=&quot;vYDA&quot;&gt;In case of &lt;code&gt;ModelView&lt;/code&gt; you should pass &lt;/p&gt;
  &lt;pre id=&quot;ahW8&quot; data-lang=&quot;cpp&quot;&gt;const mat4x4 model_view{ object.transform * camera.make_view() };
const mat3x3 normal_uniform{
    transpose(inverse(mat3x3{ model_view }))
};
pipeline.set_uniform(&amp;quot;u_model_view&amp;quot;, model_view);
pipeline.set_uniform(&amp;quot;u_normal_uniform&amp;quot;, normal_uniform);&lt;/pre&gt;
  &lt;p id=&quot;POuJ&quot;&gt;And it&amp;#x27;s useful to know that you don&amp;#x27;t even need to transpose it by yourself since the OpenGL has the &lt;code&gt;glUniformMatrix*&lt;/code&gt; methods with transposing option:&lt;/p&gt;
  &lt;pre id=&quot;nKQR&quot; data-lang=&quot;c&quot;&gt;void glUniformMatrix3fv(
    GLint location,
    GLsizei count,
    GLboolean transpose, // You could use it!
    const GLfloat *value
);&lt;/pre&gt;
  &lt;p id=&quot;AM21&quot;&gt;So:&lt;/p&gt;
  &lt;pre id=&quot;I5Z4&quot; data-lang=&quot;cpp&quot;&gt;const mat4x4 model_view{ object.transform * camera.make_view() };
const mat3x3 normal_uniform{
    transpose()
};
pipeline.set_uniform(&amp;quot;u_model_view&amp;quot;, model_view);
pipeline.set_uniform(&amp;quot;u_normal_uniform&amp;quot;,
    inverse(mat3x3{ model_view }),
    { .transpose = true }
);&lt;/pre&gt;
  &lt;h3 id=&quot;tEaM&quot;&gt;Final possible vertex shaders&lt;/h3&gt;
  &lt;ul id=&quot;fbXQ&quot;&gt;
    &lt;li id=&quot;bDDl&quot;&gt;If you just need your code, &lt;a href=&quot;https://pastecode.io/s/9scbv1ci&quot; target=&quot;_blank&quot;&gt;here it is&lt;/a&gt;&lt;/li&gt;
    &lt;li id=&quot;bGPy&quot;&gt;Or, if you&amp;#x27;re super optimizer, you could take the &lt;a href=&quot;https://pastecode.io/s/6isx6meo&quot; target=&quot;_blank&quot;&gt;mode-view version&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
  &lt;p id=&quot;gz8M&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;q6zw&quot;&gt;Base model and its modifications&lt;/h2&gt;
  &lt;p id=&quot;3lBJ&quot;&gt;Before we start to cut the Phong-Based lighting into the components, there&amp;#x27;s an important idea we need to highlight:&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(170, 33%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;0o80&quot; data-align=&quot;center&quot;&gt;The &lt;strong&gt;Base Model&lt;/strong&gt; could be modified to get &lt;em&gt;different light sources&lt;/em&gt;&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;yU6g&quot;&gt;That means that the description below explains the base model, which doesn&amp;#x27;t give us realistic light, but modifying this base model by adding the attenuation by distance or changing the material and light properties we could reach the better lighting.&lt;/p&gt;
  &lt;p id=&quot;dEQY&quot;&gt;&lt;/p&gt;
  &lt;h2 id=&quot;G0Xu&quot;&gt;The Light Components&lt;/h2&gt;
  &lt;p id=&quot;WuvI&quot;&gt;The Phong Light is simulation. You&amp;#x27;d hear a thousand times that it&amp;#x27;s impossible to calculate the real lighting behavior using computers. So, we just lie to each other and happily continue to develop.&lt;/p&gt;
  &lt;p id=&quot;elMo&quot;&gt;The Phong Light contains of 3 great deceptions:&lt;/p&gt;
  &lt;figure id=&quot;js0a&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/8a/a5/8aa554ab-50dd-4122-89f2-0b073fc7d000.png&quot; width=&quot;540&quot; /&gt;
    &lt;figcaption&gt;Here they are! Three Great Deceptions!&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;h3 id=&quot;kfll&quot;&gt;Ambient Lie&lt;/h3&gt;
  &lt;p id=&quot;uor4&quot;&gt;The &lt;strong&gt;AMBIENT&lt;/strong&gt; lies that there&amp;#x27;s no million times reflected light from the Sun or the Moon. If you power off all the light sources inside your home at night and cover all the windows at night, you&amp;#x27;re probably insane, you know? Anyway, I did it as well and after 30 minutes noticed that I actually can see. Maybe it&amp;#x27;s the brains trick, but I guess it&amp;#x27;s the ambient light.&lt;/p&gt;
  &lt;p id=&quot;ONxV&quot;&gt;In the graphics programming, we just take the &lt;em&gt;global light color&lt;/em&gt; and multiply it by the &lt;em&gt;surface scale factor&lt;/em&gt;. Like the surface itself is the source of light!&lt;/p&gt;
  &lt;p id=&quot;SrzN&quot;&gt;If the &lt;em&gt;global light color&lt;/em&gt; is obvious, so what about the &lt;em&gt;surface scale factor&lt;/em&gt;?&lt;/p&gt;
  &lt;p id=&quot;MwON&quot;&gt;&lt;strong&gt;&lt;em&gt;Surface scale factor&lt;/em&gt;&lt;/strong&gt; could be as the material parameter, as such the texture or just the constant for each object in your game:&lt;/p&gt;
  &lt;pre id=&quot;CNnR&quot; data-lang=&quot;c&quot;&gt;vec3 ambient = u_material.ambient_scale * u_light.color;&lt;/pre&gt;
  &lt;p id=&quot;S6s2&quot;&gt;So, that&amp;#x27;s the easiest of light components.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;b4z3&quot; data-align=&quot;center&quot;&gt;AMBIENT is the simulation of global illumination like if the light rays were reflected thousands times.&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;FQYX&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;BQTJ&quot;&gt;Diffuse Lie&lt;/h3&gt;
  &lt;p id=&quot;1khF&quot;&gt;The &lt;strong&gt;DIFFUSE&lt;/strong&gt; is a better liar than the AMBIENT one. It simulates the &lt;em&gt;light source impact&lt;/em&gt;. All we know that &lt;strong&gt;&lt;em&gt;as closer the light source to the surface, as brighter the surface is&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
  &lt;p id=&quot;1kBw&quot;&gt;And everything gets in sense if you remember that the closest ray from the point to the surface &lt;em&gt;is perpendicular to the surface&lt;/em&gt;.&lt;/p&gt;
  &lt;p id=&quot;FR13&quot;&gt;So, it&amp;#x27;s easier to use &lt;strong&gt;dot production&lt;/strong&gt; between the &lt;em&gt;normalized vector to the light surface&lt;/em&gt; and &lt;em&gt;fragment normal&lt;/em&gt;:&lt;/p&gt;
  &lt;figure id=&quot;Sc15&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/78/70/7870bd21-46f0-4f42-8760-d62f4d474944.png&quot; width=&quot;416&quot; /&gt;
    &lt;figcaption&gt;The smaller angle means the closer point&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;UmcJ&quot;&gt;If you take a look at this image, you&amp;#x27;ll notice that the (1) angle between normal and light direction is smaller than the (2) second one. And same relation with the distance.&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;q4uN&quot; data-align=&quot;center&quot;&gt;The closer the to-light-vector to the normal, the higher the cosine and light impact as well&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;8ys3&quot;&gt;&lt;/p&gt;
  &lt;figure id=&quot;WcbZ&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/34/fb/34fb73fb-bf0e-4826-94aa-f967ba6e294b.png&quot; width=&quot;555&quot; /&gt;
    &lt;figcaption&gt;The dot production is super useful! At least when your vectors are normalized...&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;4ZaY&quot;&gt;If we assume that the &lt;strong&gt;normal&lt;/strong&gt; is X axis and the &lt;strong&gt;dot production&lt;/strong&gt; of to_light vector to normal gives us the nice scale factor, excepting it &lt;em&gt;could be negative.&lt;/em&gt;&lt;/p&gt;
  &lt;p id=&quot;rirs&quot;&gt;To cut the negative scale, we just could use the max function:&lt;/p&gt;
  &lt;pre id=&quot;V8Tk&quot; data-lang=&quot;c&quot;&gt;// to_light and normal ARE normalized!!!!
float diffuse_impact = max(dot(to_light, normal), 0.0);&lt;/pre&gt;
  &lt;p id=&quot;oQK4&quot;&gt;But why can&amp;#x27;t we just use the distance? The answer is pretty straightforward:&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;dAdl&quot; data-align=&quot;center&quot;&gt;We use cosine instead of distance to not pass the &amp;quot;max_distance&amp;quot; and to not interpolate between them to get the [o.O; 1.0] impact scale factor, but you could!&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;CZdG&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;u4YW&quot;&gt;So, what to do with it? Well, you could scale your diffuse texture color to this factor or you could take the material color and scale it as well. Here&amp;#x27;s the calculation pipeline:&lt;/p&gt;
  &lt;figure id=&quot;bk79&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/fc/72/fc72ed46-068d-4ca4-94b6-5ee5b46f05b3.png&quot; width=&quot;883&quot; /&gt;
    &lt;figcaption&gt;Diffuse calculation pipeline&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;U47J&quot;&gt;The 4 step could be changed as you want. Instead of light color, you could use the material or texel (texture pixel):&lt;/p&gt;
  &lt;ol id=&quot;MfCg&quot;&gt;
    &lt;li id=&quot;ajIV&quot;&gt;&lt;code&gt;vec3 to_light_direction = normalize(u_light.position - frag_position);&lt;/code&gt;&lt;br /&gt;That how it looks like in the fragment shader. The normalization is required!&lt;/li&gt;
    &lt;li id=&quot;u6b0&quot;&gt;&lt;code&gt;float diffuse_impact = max(dot(to_light_direction, normal), 0.0);&lt;/code&gt;&lt;br /&gt;And the second step to get the scale factor. With this scale, we could make the color darker if the angle is too high;&lt;/li&gt;
    &lt;li id=&quot;5eeT&quot;&gt;&lt;code&gt;vec3 diffuse = texture(u_diffuse_texture, frag_uv).rgb * diffuse_impact;&lt;/code&gt;&lt;/li&gt;
  &lt;/ol&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;f7sP&quot; data-align=&quot;center&quot;&gt;We don&amp;#x27;t need to colorize on this step by color of light. We only scale the fragment color from the black to the actual color:&lt;/p&gt;
    &lt;p id=&quot;I3h0&quot; data-align=&quot;center&quot;&gt;&lt;code&gt;[0.0, o.O, 0.0] --diffuse--&amp;gt; [R, G, B]&lt;/code&gt;&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;rig1&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;BhR3&quot;&gt;If you have multiply sources light, you can sum the factor! But be careful. You could, but probably don&amp;#x27;t want to have the scale greater than &lt;code&gt;1.0&lt;/code&gt;.&lt;/p&gt;
  &lt;p id=&quot;FS5I&quot;&gt;If you&amp;#x27;re thinking about the distance, continue to read this summary. It&amp;#x27;s just subtype or modification of this base lies.&lt;/p&gt;
  &lt;p id=&quot;d6bl&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;mzvK&quot;&gt;Specular Lie&lt;/h3&gt;
  &lt;p id=&quot;8NwU&quot;&gt;As you could notice, the diffuse light doesn&amp;#x27;t actually highlight the object as the lighting usually does. It slightly turns pixels on when the light source is getting closer to the surface, but what about &lt;strong&gt;&lt;em&gt;the patch of reflected light&lt;/em&gt;&lt;/strong&gt;?&lt;/p&gt;
  &lt;p id=&quot;DRIO&quot;&gt;The &lt;strong&gt;SPECULAR&lt;/strong&gt; pretends the reflected light. Well, it actually doesn&amp;#x27;t really pretend like other light components. The reason of pointless honest is that we actually need to find the reflected vector of the light. Let&amp;#x27;s figure out!&lt;/p&gt;
  &lt;p id=&quot;VSov&quot;&gt;The specular &amp;quot;prints&amp;quot; on the texture the mostly lighting color. You could see it in real live as well:&lt;/p&gt;
  &lt;figure id=&quot;sgw3&quot; class=&quot;m_column&quot;&gt;
    &lt;img src=&quot;https://img4.teletype.in/files/b3/1e/b31e4736-f7fe-464d-959c-6cc69c5f024f.jpeg&quot; width=&quot;3024&quot; /&gt;
    &lt;figcaption&gt;The book has its own texture, but specular just put the color of light source on it.&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;I9yE&quot;&gt;Look at that book. The center of light reflection is mostly the color of light source, and as closer the reflection direction to our eyes, as brighter it is:&lt;/p&gt;
  &lt;figure id=&quot;GaMi&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/d1/6f/d16f8000-b7c2-4303-b3b6-b4b1cee92e14.png&quot; width=&quot;416&quot; /&gt;
    &lt;figcaption&gt;The main characters of specular game&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;FpYF&quot;&gt;That&amp;#x27;s the identical idea to the diffuse impact, but the directional vector to the camera position plays the role of normal vector, while the &amp;quot;to-light&amp;quot; role was taken by the reflected light direction. Sounds hard? Well, let&amp;#x27;s summarize:&lt;/p&gt;
  &lt;section style=&quot;background-color:hsl(hsl(263, 48%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;YVWQ&quot; data-align=&quot;center&quot;&gt;The closer the reflected light kicks your eye, the more pain you feel&lt;br /&gt;==&lt;br /&gt;The less the angle between the reflected light vector and vector to your eye, the harder it hurts&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;F92d&quot;&gt;So, the calculation pipeline is a little harder than diffuse, but I just add some mess to be clear, so it just looks like that:&lt;/p&gt;
  &lt;figure id=&quot;d41s&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img2.teletype.in/files/df/ab/dfabad1e-ed32-4e88-b924-8513ae36b146.png&quot; width=&quot;883&quot; /&gt;
    &lt;figcaption&gt;Specular component calculation pipeline&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;ol id=&quot;pQsy&quot;&gt;
    &lt;li id=&quot;0DTI&quot;&gt;The first step is getting the reflection itself. To do so, we could take our &lt;code&gt;to_light&lt;/code&gt; vector, reverse it by multiplying on -1 (or just &lt;code&gt;-to_light&lt;/code&gt;) and applying a &lt;strong&gt;reflect function&lt;/strong&gt; relative to fragment &lt;code&gt;normal&lt;/code&gt;:&lt;br /&gt;&lt;code&gt;vec3 reflection = reflect(-to_light, normal);&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;FlNu&quot;&gt;Then, we need to find the vector to the &lt;strong&gt;view position&lt;/strong&gt;. It&amp;#x27;s as simple as find the &lt;code&gt;to_light&lt;/code&gt; direction:&lt;/li&gt;
    &lt;ol id=&quot;Nawf&quot;&gt;
      &lt;li id=&quot;hM3d&quot;&gt;If you use &lt;code&gt;u_model_view&lt;/code&gt; matrix, you don&amp;#x27;t need any other information:&lt;br /&gt;&lt;code&gt;vec3 to_view = normalize(/* camera_pos */ -frag_position);&lt;/code&gt;&lt;/li&gt;
      &lt;li id=&quot;53Lx&quot;&gt;Otherwise, you need to provide the camera position as the &lt;code&gt;uniform&lt;/code&gt; parameter:&lt;br /&gt;&lt;code&gt;vec3 to_view = normalize(u_camera_pos - frag_position);&lt;/code&gt;&lt;/li&gt;
    &lt;/ol&gt;
    &lt;li id=&quot;9YLW&quot;&gt;Then, we could find the &lt;em&gt;specular impact&lt;/em&gt;, aka cosine using &lt;strong&gt;dot production&lt;/strong&gt;:&lt;br /&gt;&lt;code&gt;float specular_impact = max(dot(reflection, to_view), 0.0);&lt;/code&gt;&lt;/li&gt;
    &lt;li id=&quot;FqD1&quot;&gt;The angle between two vectors is linear, but we see the light reflection only in a small area, so, to make it the light color concentrated in the center of reflection, we need to apply the &lt;strong&gt;reform function&lt;/strong&gt;. It&amp;#x27;s usually the &lt;strong&gt;power&lt;/strong&gt; function.&lt;/li&gt;
  &lt;/ol&gt;
  &lt;p id=&quot;e5J6&quot;&gt;&lt;br /&gt;&lt;strong&gt;&lt;em&gt;Shininess&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
  &lt;p id=&quot;SMzj&quot;&gt;The shininess is an actually the parameter of &lt;strong&gt;reform function&lt;/strong&gt;, in our and common case is power value. Let&amp;#x27;s take a look at this gif:&lt;/p&gt;
  &lt;figure id=&quot;emgQ&quot; class=&quot;m_original&quot;&gt;
    &lt;img src=&quot;https://img1.teletype.in/files/c2/3e/c23e1d4b-c0a2-4f00-a6ba-a3f89fe4b76e.gif&quot; width=&quot;600&quot; /&gt;
    &lt;figcaption&gt;Changing &amp;quot;shininess&amp;quot; parameter of power function&lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;p id=&quot;Epjj&quot;&gt;The X coordinate is the &lt;code&gt;specular_impact&lt;/code&gt; value, while the Y coordinate is the result of &lt;code&gt;pow(specular_impact, u_material.shininess)&lt;/code&gt;:&lt;/p&gt;
  &lt;ul id=&quot;pyez&quot;&gt;
    &lt;li id=&quot;IDbG&quot;&gt;The higher the shininess, the faster the power result is 0.0;&lt;/li&gt;
    &lt;li id=&quot;3euU&quot;&gt;That&amp;#x27;s mean that we transform the linear angle to cubic form and our light glint won&amp;#x27;t be too big&lt;/li&gt;
  &lt;/ul&gt;
  &lt;section style=&quot;background-color:hsl(hsl(199, 50%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;WWKH&quot; data-align=&quot;center&quot;&gt;The higher the shininess cause more concentrated light &lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;UnGu&quot;&gt;&lt;/p&gt;
  &lt;h3 id=&quot;2HR8&quot;&gt;Combination of Lies&lt;/h3&gt;
  &lt;p id=&quot;FzQk&quot;&gt;When you get what&amp;#x27;s each component of phong light lies about, you could remember that we need to sum those components, and that&amp;#x27;s the basic light:&lt;/p&gt;
  &lt;p id=&quot;MBCb&quot;&gt;&lt;strong&gt;&lt;em&gt;Example with static material and a single texture: &lt;/em&gt;&lt;/strong&gt;&lt;a href=&quot;https://pastecode.io/s/uf2yg92m&quot; target=&quot;_blank&quot;&gt;(link)&lt;/a&gt;&lt;/p&gt;
  &lt;pre id=&quot;VKQT&quot; data-lang=&quot;c&quot;&gt;#version 300 core

layout(location = 0) in vec3 frag_position;
layout(location = 1) in vec3 frag_normal;
layout(location = 2) in vec2 frag_uv;

layout(location = 0) out vec4 frag_color;

struct Light {
    vec3 position;
    vec3 color;
};

struct Material {
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float shininess;
};

uniform sampler2D u_texture;
uniform Light     u_light;
uniform Material  u_material;
uniform vec3      u_view_position; // ONLY IF WE DON&amp;#x27;T USE MODEL_VIEW!

void main() {
    vec3 normal = normalize(frag_normal);
    vec3 texel = texture(u_texture, frag_uv).rgb;

    // AMBIENT COMPONENT
    vec3 ambient = u_material.ambient * u_light.color; // you could use texel as well

    // DIFFUSE COMPONENT
    vec3 to_light = normalize(u_light.position - frag_position);
    float diffuse_impact = max(dot(to_light, normal), 0.0);
    vec3 diffuse = u_material.diffuse * diffuse_impact;

    // SPECULAR COMPONENT
    vec3 reflected = normalize(reflect(-to_light, normal));
    vec3 to_eye = normalize(u_view_position - frag_position); // ONLY IF WE DON&amp;#x27;T USE MODEL_VIEW!
    // vec3 to_eye = normalize(-frag_position); // If we use MODEL_VIEW
    float specular_impact = max(dot(reflected, to_eye), 0.0);
    float specular_reformed = pow(specular_impact, u_material.shininess);
    vec3 specular = u_material.specular * u_light.color * specular_reformed;

    // GETTING RESULT FRAGMENT COLOR:
    frag_color = vec4((ambient + diffuse + specular) * texel, 1.0);
}&lt;/pre&gt;
  &lt;section style=&quot;background-color:hsl(hsl(0, 0%, var(--autocolor-background-lightness, 95%)), 85%, 85%);&quot;&gt;
    &lt;p id=&quot;BIIx&quot; data-align=&quot;center&quot;&gt;Notice that if we transform everything with view transform, the eyes position will be the center of the coordinate system. So, in case of using ModelView method we don&amp;#x27;t need to pass the camera position and calculate the &lt;code&gt;to_eye&lt;/code&gt; vector by subtracting from [0, 0, 0] vector, which is just a vector reversing&lt;/p&gt;
  &lt;/section&gt;
  &lt;p id=&quot;Hmmw&quot;&gt;The example with textured materials you &lt;a href=&quot;https://pastecode.io/s/kmk5ao0q&quot; target=&quot;_blank&quot;&gt;could find here&lt;/a&gt;!&lt;/p&gt;
  &lt;p id=&quot;VmA1&quot;&gt;&lt;/p&gt;
  &lt;p id=&quot;3HRq&quot;&gt;This light is super fake since it doesn&amp;#x27;t count the real light distance or power of light source, but this is the skeleton of the lighting. You could modify, add or delete some properties to each component to achieve the better lights. And we&amp;#x27;ll take a look at those modifications in the next topic!&lt;/p&gt;
  &lt;h2 id=&quot;VALH&quot;&gt;What&amp;#x27;s next?&lt;/h2&gt;
  &lt;p id=&quot;CNzq&quot;&gt;This summary described the base model. Anyway, there are a lot of unanswered questions left behind. I&amp;#x27;m tired to write this exact page so, I&amp;#x27;ll continue after a little break and answer those questions later:&lt;/p&gt;
  &lt;ul id=&quot;srXr&quot;&gt;
    &lt;li id=&quot;Pv9H&quot;&gt;How to restrict the light to get the local lighting source like a lamp or torch? What about the Sun lighting and of course I want to have a player&amp;#x27;s flashlight!&lt;br /&gt;(&lt;em&gt;Directional, Point and Spotlight&lt;/em&gt;);&lt;/li&gt;
    &lt;li id=&quot;hhER&quot;&gt;How to push multiply lighting sources to the scene and how to combine them inside the shaders? How those fucking game engines handle thousand of light sources!?&lt;/li&gt;
    &lt;li id=&quot;CpCE&quot;&gt;Why someone adds the ambient, diffuse and specular to the light structure?&lt;/li&gt;
    &lt;li id=&quot;ZANg&quot;&gt;How to make the lighting more realistic? (&lt;em&gt;Blinn-Phong, Shadows&lt;/em&gt;)?&lt;/li&gt;
  &lt;/ul&gt;

</content></entry></feed>