LearnOpenGL

Translation in progress of learnopengl.com.
git clone https://git.mtkn.jp/LearnOpenGL
Log | Files | Refs

Lighting.html (33568B)


      1 <!DOCTYPE html>
      2 <html lang="ja"> 
      3 <head>
      4     <meta charset="utf-8"/>
      5     <title>LearnOpenGL</title>
      6     <link rel="shortcut icon" type="image/ico" href="/favicon.ico"  />
      7 	<link rel="stylesheet" href="../static/style.css" />
      8 	<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script>
      9 	<script src="/static/functions.js"></script>
     10 </head>
     11 <body>
     12 	<nav>
     13 <ol>
     14 	<li id="Introduction">
     15 		<a href="https://learnopengl.com/Introduction">はじめに</a>
     16 	</li>
     17 	<li id="Getting-started">
     18 		<span class="closed">入門</span>
     19 		<ol>
     20 			<li id="Getting-started/OpenGL">
     21 				<a href="https://learnopengl.com/Getting-started/OpenGL">OpenGL </a>
     22 			</li>
     23 			<li id="Getting-started/Creating-a-window">
     24 				<a href="https://learnopengl.com/Getting-started/Creating-a-window">ウィンドウの作成</a>
     25 			</li>
     26 			<li id="Getting-started/Hello-Window">
     27 				<a href="https://learnopengl.com/Getting-started/Hello-Window">最初のウィンドウ</a>
     28 			</li>
     29 			<li id="Getting-started/Hello-Triangle">
     30 				<a href="https://learnopengl.com/Getting-started/Hello-Triangle">最初の三角形</a>
     31 			</li>
     32 			<li id="Getting-started/Shaders">
     33 				<a href="https://learnopengl.com/Getting-started/Shaders">シェーダー</a>
     34 			</li>
     35 			<li id="Getting-started/Textures">
     36 				<a href="https://learnopengl.com/Getting-started/Textures">テクスチャ</a>
     37 			</li>
     38 			<li id="Getting-started/Transformations">
     39 				<a href="https://learnopengl.com/Getting-started/Transformations">座標変換</a>
     40 			</li>
     41 			<li id="Getting-started/Coordinate-Systems">
     42 				<a href="https://learnopengl.com/Getting-started/Coordinate-Systems">座標系</a>
     43 			</li>
     44 			<li id="Getting-started/Camera">
     45 				<a href="https://learnopengl.com/Getting-started/Camera">カメラ</a>
     46 			</li>
     47 			<li id="Getting-started/Review">
     48 				<a href="https://learnopengl.com/Getting-started/Review">まとめ</a>
     49 			</li>
     50 		</ol>
     51 	</li>
     52 	<li id="Lighting">
     53 		<span class="closed">Lighting </span>
     54 		<ol>
     55 			<li id="Lighting/Colors">
     56 				<a href="https://learnopengl.com/Lighting/Colors">Colors </a>
     57 			</li>
     58 			<li id="Lighting/Basic-Lighting">
     59 				<a href="https://learnopengl.com/Lighting/Basic-Lighting">Basic Lighting </a>
     60 			</li>
     61 			<li id="Lighting/Materials">
     62 				<a href="https://learnopengl.com/Lighting/Materials">Materials </a>
     63 			</li>
     64 			<li id="Lighting/Lighting-maps">
     65 				<a href="https://learnopengl.com/Lighting/Lighting-maps">Lighting maps </a>
     66 			</li>
     67 			<li id="Lighting/Light-casters">
     68 				<a href="https://learnopengl.com/Lighting/Light-casters">Light casters </a>
     69 			</li>
     70 			<li id="Lighting/Multiple-lights">
     71 				<a href="https://learnopengl.com/Lighting/Multiple-lights">Multiple lights </a>
     72 			</li>
     73 			<li id="Lighting/Review">
     74 				<a href="https://learnopengl.com/Lighting/Review">Review </a>
     75 			</li>
     76 		</ol>
     77 	</li>
     78 	<li id="Model-Loading">
     79 		<span class="closed">Model Loading </span>
     80 		<ol>
     81 			<li id="Model-Loading/Assimp">
     82 				<a href="https://learnopengl.com/Model-Loading/Assimp">Assimp </a>
     83 			</li>
     84 			<li id="Model-Loading/Mesh">
     85 				<a href="https://learnopengl.com/Model-Loading/Mesh">Mesh </a>
     86 			</li>
     87 			<li id="Model-Loading/Model">
     88 				<a href="https://learnopengl.com/Model-Loading/Model">Model </a>
     89 			</li>
     90 		</ol>
     91 	</li>
     92 	<li id="Advanced-OpenGL">
     93 		<span class="closed">Advanced OpenGL </span>
     94 		<ol>
     95 			<li id="Advanced-OpenGL/Depth-testing">
     96 				<a href="https://learnopengl.com/Advanced-OpenGL/Depth-testing">Depth testing </a>
     97 			</li>
     98 			<li id="Advanced-OpenGL/Stencil-testing">
     99 				<a href="https://learnopengl.com/Advanced-OpenGL/Stencil-testing">Stencil testing </a>
    100 			</li>
    101 			<li id="Advanced-OpenGL/Blending">
    102 				<a href="https://learnopengl.com/Advanced-OpenGL/Blending">Blending </a>
    103 			</li>
    104 			<li id="Advanced-OpenGL/Face-culling">
    105 				<a href="https://learnopengl.cm/Advanced-OpenGL/Face-culling">Face culling </a>
    106 			</li>
    107 			<li id="Advanced-OpenGL/Framebuffers">
    108 				<a href="https://learnopengl.com/Advanced-OpenGL/Framebuffers">Framebuffers </a>
    109 			</li>
    110 			<li id="Advanced-OpenGL/Cubemaps">
    111 				<a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps">Cubemaps </a>
    112 			</li>
    113 			<li id="Advanced-OpenGL/Advanced-Data">
    114 				<a href="https://learnopengl.com/Advanced-OpenGL/Advanced-Data">Advanced Data </a>
    115 			</li>
    116 			<li id="Advanced-OpenGL/Advanced-GLSL">
    117 				<a href="https://learnopengl.com/Advanced-OpenGL/Advanced-GLSL">Advanced GLSL </a>
    118 			</li>
    119 			<li id="Advanced-OpenGL/Geometry-Shader">
    120 				<a href="https://learnopengl.com/Advanced-OpenGL/Geometry-Shader">Geometry Shader </a>
    121 			</li>
    122 			<li id="Advanced-OpenGL/Instancing">
    123 				<a href="https://learnopengl.com/Advanced-OpenGL/Instancing">Instancing </a>
    124 			</li>
    125 			<li id="Advanced-OpenGL/Anti-Aliasing">
    126 				<a href="https://learnopengl.com/Advanced-OpenGL/Anti-Aliasing">Anti Aliasing </a>
    127 			</li>
    128 		</ol>
    129 	</li>
    130 	<li id="Advanced-Lighting">
    131 		<span class="closed">Advanced Lighting </span>
    132 		<ol>
    133 			<li id="Advanced-Lighting/Advanced-Lighting">
    134 				<a href="https://learnopengl.com/Advanced-Lighting/Advanced-Lighting">Advanced Lighting </a>
    135 			</li>
    136 			<li id="Advanced-Lighting/Gamma-Correction">
    137 				<a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction">Gamma Correction </a>
    138 			</li>
    139 			<li id="Advanced-Lighting/Shadows">
    140 				<span class="closed">Shadows </span>
    141 				<ol>
    142 					<li id="Advanced-Lighting/Shadows/Shadow-Mapping">
    143 						<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">Shadow Mapping </a>
    144 					</li>
    145 					<li id="Advanced-Lighting/Shadows/Point-Shadows">
    146 						<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Point-Shadows">Point Shadows </a>
    147 					</li>
    148 				</ol>
    149 			</li>
    150 			<li id="Advanced-Lighting/Normal-Mapping">
    151 				<a href="https://learnopengl.com/Advanced-Lighting/Normal-Mapping">Normal Mapping </a>
    152 			</li>
    153 			<li id="Advanced-Lighting/Parallax-Mapping">
    154 				<a href="https://learnopengl.com/Advanced-Lighting/Parallax-Mapping">Parallax Mapping </a>
    155 			</li>
    156 			<li id="Advanced-Lighting/HDR">
    157 				<a href="https://learnopengl.com/Advanced-Lighting/HDR">HDR </a>
    158 			</li>
    159 			<li id="Advanced-Lighting/Bloom">
    160 				<a href="https://learnopengl.com/Advanced-Lighting/Bloom">Bloom </a>
    161 			</li>
    162 			<li id="Advanced-Lighting/Deferred-Shading">
    163 				<a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">Deferred Shading </a>
    164 			</li>
    165 			<li id="Advanced-Lighting/SSAO">
    166 				<a href="https://learnopengl.com/Advanced-Lighting/SSAO">SSAO </a>
    167 			</li>
    168 		</ol>
    169 	</li>
    170 	<li id="PBR">
    171 		<span class="closed">PBR </span>
    172 		<ol>
    173 			<li id="PBR/Theory">
    174 				<a href="https://learnopengl.com/PBR/Theory">Theory </a>
    175 			</li>
    176 			<li id="PBR/Lighting">
    177 				<a href="https://learnopengl.com/PBR/Lighting">Lighting </a>
    178 			</li>
    179 			<li id="PBR/IBL">
    180 				<span class="closed">IBL </span>
    181 				<ol>
    182 					<li id="PBR/IBL/Diffuse-irradiance">
    183 						<a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance">Diffuse irradiance </a>
    184 					</li>
    185 					<li id="PBR/IBL/Specular-IBL">
    186 						<a href="https://learnopengl.com/PBR/IBL/Specular-IBL">Specular IBL </a>
    187 					</li>
    188 				</ol>
    189 			</li>
    190 		</ol>
    191 	</li>
    192 	<li id="In-Practice">
    193 		<span class="closed">In Practice </span>
    194 		<ol>
    195 			<li id="In-Practice/Debugging">
    196 				<a href="https://learnopengl.com/In-Practice/Debugging">Debugging </a>
    197 			</li>
    198 			<li id="In-Practice/Text-Rendering">
    199 				<a href="https://learnopengl.com/In-Practice/Text-Rendering">Text Rendering </a>
    200 			</li>
    201 			<li id="In-Practice/2D-Game">
    202 				<span class="closed">2D Game </span>
    203 				<ol>
    204 					<li id="In-Practice/2D-Game/Breakout">
    205 						<a href="https://learnopengl.com/In-Practice/2D-Game/Breakout">Breakout </a>
    206 					</li>
    207 					<li id="In-Practice/2D-Game/Setting-up">
    208 						<a href="https://learnopengl.com/In-Practice/2D-Game/Setting-up">Setting up </a>
    209 					</li>
    210 					<li id="In-Practice/2D-Game/Rendering-Sprites">
    211 						<a href="https://learnopengl.com/In-Practice/2D-Game/Rendering-Sprites">Rendering Sprites </a>
    212 					</li>
    213 					<li id="In-Practice/2D-Game/Levels">
    214 						<a href="https://learnopengl.com/In-Practice/2D-Game/Levels">Levels </a>
    215 					</li>
    216 					<li id="In-Practice/2D-Game/Collisions">
    217 						<span class="closed">Collisions </span>
    218 						<ol>
    219 							<li id="In-Practice/2D-Game/Collisions/Ball">
    220 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Ball">Ball </a>
    221 							</li>
    222 							<li id="In-Practice/2D-Game/Collisions/Collision-detection">
    223 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-detection">Collision detection </a>
    224 							</li>
    225 							<li id="In-Practice/2D-Game/Collisions/Collision-resolution">
    226 								<a href="https://learnopengl.com/In-Practice/2D-Game/Collisions/Collision-resolution">Collision resolution </a>
    227 							</li>
    228 						</ol>
    229 					</li>
    230 					<li id="In-Practice/2D-Game/Particles">
    231 						<a href="https://learnopengl.com/In-Practice/2D-Game/Particles">Particles </a>
    232 					</li>
    233 					<li id="In-Practice/2D-Game/Postprocessing">
    234 						<a href="https://learnopengl.com/In-Practice/2D-Game/Postprocessing">Postprocessing </a>
    235 					</li>
    236 					<li id="In-Practice/2D-Game/Powerups">
    237 						<a href="https://learnopengl.com/In-Practice/2D-Game/Powerups">Powerups </a>
    238 					</li>
    239 					<li id="In-Practice/2D-Game/Audio">
    240 						<a href="https://learnopengl.com/In-Practice/2D-Game/Audio">Audio </a>
    241 					</li>
    242 					<li id="In-Practice/2D-Game/Render-text">
    243 						<a href="https://learnopengl.com/In-Practice/2D-Game/Render-text">Render text </a>
    244 					</li>
    245 					<li id="In-Practice/2D-Game/Final-thoughts">
    246 						<a href="https://learnopengl.com/In-Practice/2D-Game/Final-thoughts">Final thoughts </a>
    247 					</li>
    248 				</ol>
    249 			</li>
    250 		</ol>
    251 	</li>
    252 	<li id="Guest-Articles">
    253 		<span class="closed">Guest Articles </span>
    254 		<ol>
    255 			<li id="Guest-Articles/How-to-publish">
    256 				<a href="https://learnopengl.com/Guest-Articles/How-to-publish">How to publish </a>
    257 			</li>
    258 			<li id="Guest-Articles/2020">
    259 				<span class="closed">2020 </span>
    260 				<ol>
    261 					<li id="Guest-Articles/2020/OIT">
    262 						<span class="closed">OIT </span>
    263 						<ol>
    264 							<li id="Guest-Articles/2020/OIT/Introduction">
    265 								<a href="https://learnopengl.com/Guest-Articles/2020/OIT/Introduction">Introduction </a>
    266 							</li>
    267 							<li id="Guest-Articles/2020/OIT/Weighted-Blended">
    268 								<a href="https://learnopengl.com/Guest-Articles/2020/OIT/Weighted-Blended">Weighted Blended </a>
    269 							</li>
    270 						</ol>
    271 					</li>
    272 					<li id="Guest-Articles/2020/Skeletal-Animation">
    273 						<a href="https://learnopengl.com/Guest-Articles/2020/Skeletal-Animation">Skeletal Animation </a>
    274 					</li>
    275 				</ol>
    276 			</li>
    277 			<li id="Guest-Articles/2021">
    278 				<span class="closed">2021 </span>
    279 				<ol>
    280 					<li id="Guest-Articles/2021/CSM">
    281 						<a href="https://learnopengl.com/Guest-Articles/2021/CSM">CSM </a>
    282 					</li>
    283 					<li id="Guest-Articles/2021/Scene">
    284 						<span class="closed">Scene </span>
    285 						<ol>
    286 							<li id="Guest-Articles/2021/Scene/Scene-Graph">
    287 								<a href="https://learnopengl.com/Guest-Articles/2021/Scene/Scene-Graph">Scene Graph </a>
    288 							</li>
    289 							<li id="Guest-Articles/2021/Scene/Frustum-Culling">
    290 								<a href="https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling">Frustum Culling </a>
    291 							</li>
    292 						</ol>
    293 					</li>
    294 					<li id="Guest-Articles/2021/Tessellation">
    295 						<span class="closed">Tessellation </span>
    296 						<ol>
    297 							<li id="Guest-Articles/2021/Tessellation/Height-map">
    298 								<a href="https://learnopengl.com/Guest-Articles/2021/Tessellation/Height-map">Height map </a>
    299 							</li>
    300 						</ol>
    301 					</li>
    302 				</ol>
    303 			</li>
    304 		</ol>
    305 	</li>
    306 	<li id="Code-repository">
    307 		<a href="https://learnopengl.com/Code-repository">Code repository </a>
    308 	</li>
    309 	<li id="Translations">
    310 		<a href="https://learnopengl.com/Translations">Translations </a>
    311 	</li>
    312 	<li id="About">
    313 		<a href="https://learnopengl.com/About">About </a>
    314 	</li>
    315 </ol>
    316 	</nav>
    317 	<main>
    318     <h1 id="content-title">Lighting</h1>
    319 <h1 id="content-url" style='display:none;'>PBR/Lighting</h1>
    320 <p>
    321   In the <a href="https://learnopengl.com/PBR/Theory" target="_blank">previous</a> chapter we laid the foundation for getting a realistic physically based renderer off the ground. In this chapter we'll focus on translating the previously discussed theory into an actual renderer that uses direct (or analytic) light sources: think of point lights, directional lights, and/or spotlights. 
    322 </p>
    323 
    324 <p>
    325   Let's start by re-visiting the final reflectance equation from the previous chapter:
    326 </p>
    327 
    328  \[
    329     L_o(p,\omega_o) = \int\limits_{\Omega} 
    330     	(k_d\frac{c}{\pi} + \frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)})
    331     	L_i(p,\omega_i) n \cdot \omega_i  d\omega_i
    332  \]
    333 
    334 <p>
    335   We now know mostly what's going on, but what still remained a big unknown is how exactly we're going to represent irradiance, the total radiance \(L\), of the scene. We know that radiance \(L\) (as interpreted in computer graphics land) measures the radiant flux \(\phi\) or light energy of a light source over a given solid angle  \(\omega\). In our case we assumed the solid angle \(\omega\) to be infinitely small in which case radiance measures the flux of a light source over a single light ray or direction vector.
    336 </p>
    337 
    338 <p>
    339   Given this knowledge, how do we translate this into some of the lighting knowledge we've accumulated from previous chapters? Well, imagine we have a single point light (a light source that shines equally bright in all directions) with a radiant flux of <code>(23.47, 21.31, 20.79)</code> as translated to an RGB triplet. The radiant intensity of this light source equals its radiant flux at all outgoing direction rays. However, when shading a specific point \(p\) on a surface, of all possible incoming light directions over its hemisphere \(\Omega\), only one incoming direction vector \(w_i\) directly comes from the point light source. As we only have a single light source in our scene, assumed to be a single point in space, all other possible incoming light directions have zero radiance observed over the surface point \(p\):
    340 </p>
    341 
    342 <img src="/img/pbr/lighting_radiance_direct.png" class="clean" alt="Radiance on a point p of a non-attenuated point light source only returning non-zero at the infitely small solid angle Wi or light direction vector Wi"/>
    343 
    344 <p>
    345   If at first, we assume that light attenuation (dimming of light over distance) does not affect the point light source, the radiance of the incoming light ray is the same regardless of where we position the light (excluding scaling the radiance by the incident angle \(\cos  \theta\)). This, because the point light has the same radiant intensity regardless of the angle we look at it, effectively modeling its radiant intensity as its radiant flux: a constant vector <code>(23.47, 21.31, 20.79)</code>.
    346 </p>
    347 
    348 <p>
    349   However, radiance also takes a position \(p\) as input and as any realistic point light source takes light attenuation into account, the radiant intensity of the point light source is scaled by some measure of the distance between point \(p\) and the light source. Then, as extracted from the original radiance equation, the result is scaled by the dot product between the surface normal \(n\) and the incoming light direction \(w_i\).
    350 </p>
    351 
    352 <p>
    353   To put this in more practical terms: in the case of a direct point light the radiance function \(L\)  measures the light color, attenuated over its distance to \(p\) and scaled by \(n \cdot w_i\), but only over the single light ray \(w_i\) that hits \(p\) which equals the light's direction vector from \(p\).
    354   In code this translates to:
    355 </p>
    356 
    357 <pre><code>
    358 vec3  lightColor  = vec3(23.47, 21.31, 20.79);
    359 vec3  wi          = normalize(lightPos - fragPos);
    360 float cosTheta    = max(dot(N, Wi), 0.0);
    361 float attenuation = calculateAttenuation(fragPos, lightPos);
    362 vec3  radiance    = lightColor * attenuation * cosTheta;
    363 </code></pre>
    364   
    365 <p>
    366   Aside from the different terminology, this piece of code should be awfully familiar to you: this is exactly how we've been doing diffuse lighting so far. When it comes to direct lighting, radiance is calculated similarly to how we've calculated lighting before as only a single light direction vector contributes to the surface's radiance. 
    367 </p>
    368   
    369 <note>
    370   Note that this assumption holds as point lights are infinitely small and only a single point in space. If we were to model a light that has area or volume, its radiance would be non-zero in more than one incoming light direction.
    371 </note>
    372   
    373 <p>
    374   For other types of light sources originating from a single point we calculate radiance similarly. For instance, a directional light source has a constant \(w_i\) without an attenuation factor. And a spotlight would not have a constant radiant intensity, but one that is scaled by the forward direction vector of the spotlight. 
    375 </p>
    376   
    377 <p>
    378   This also brings us back to the integral \(\int\) over the surface's hemisphere \(\Omega\) . As we know beforehand the single locations of all the contributing light sources while shading a single surface point, it is not required to try and solve the integral. We can directly take the (known) number of light sources and calculate their total irradiance, given that each light source has only a single light direction that influences the surface's radiance. This makes PBR on direct light sources relatively simple as we effectively only have to loop over the contributing light sources. When we later take environment lighting into account in the <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">IBL</a> chapters we do have to take the integral into account as light can come from any direction.
    379 </p>
    380   
    381 <h2>A PBR surface model</h2>
    382 <p>
    383   Let's start by writing a fragment shader that implements the previously described PBR models. First, we need to take the relevant PBR inputs required for shading the surface:
    384 </p>
    385   
    386 <pre><code>
    387 #version 330 core
    388 out vec4 FragColor;
    389 in vec2 TexCoords;
    390 in vec3 WorldPos;
    391 in vec3 Normal;
    392   
    393 uniform vec3 camPos;
    394   
    395 uniform vec3  albedo;
    396 uniform float metallic;
    397 uniform float roughness;
    398 uniform float ao;
    399 </code></pre>
    400   
    401 <p>
    402   We take the standard inputs as calculated from a generic vertex shader and a set of constant material properties over the surface of the object. 
    403 </p>
    404   
    405 <p>
    406   Then at the start of the fragment shader we do the usual calculations required for any lighting algorithm:
    407 </p>
    408   
    409 <pre><code>
    410 void main()
    411 {
    412     vec3 N = normalize(Normal); 
    413     vec3 V = normalize(camPos - WorldPos);
    414     [...]
    415 }
    416 </code></pre>
    417   
    418 <h3>Direct lighting</h3>
    419 <p>
    420   In this chapter's example demo we have a total of 4 point lights that together represent the scene's irradiance. To satisfy the reflectance equation we loop over each light source, calculate its individual radiance and sum its contribution scaled by the BRDF and the light's incident angle. We can think of the loop as solving the integral \(\int\) over \(\Omega\) for direct light sources. First, we calculate the relevant per-light variables:
    421 </p>
    422   
    423 <pre><code>
    424 vec3 Lo = vec3(0.0);
    425 for(int i = 0; i &lt; 4; ++i) 
    426 {
    427     vec3 L = normalize(lightPositions[i] - WorldPos);
    428     vec3 H = normalize(V + L);
    429   
    430     float distance    = length(lightPositions[i] - WorldPos);
    431     float attenuation = 1.0 / (distance * distance);
    432     vec3 radiance     = lightColors[i] * attenuation; 
    433     [...]  
    434 </code></pre>
    435   
    436 <p>
    437   As we calculate lighting in linear space (we'll <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction" target="_blank">gamma correct</a> at the end of the shader) we attenuate the light sources by the more physically correct <def>inverse-square law</def>. 
    438 </p>
    439   
    440 <note>
    441   While physically correct, you may still want to use the constant-linear-quadratic attenuation equation that (while not physically correct) can offer you significantly more control over the light's energy falloff.
    442 </note>
    443   
    444 <p>
    445   Then, for each light we want to calculate the full Cook-Torrance specular BRDF term:
    446 </p>
    447 
    448   \[
    449   	\frac{DFG}{4(\omega_o \cdot n)(\omega_i \cdot n)}
    450   \]
    451   
    452 <p>
    453   The first thing we want to do is calculate the ratio between specular and diffuse reflection, or how much the surface reflects light versus how much it refracts light. We know from the <a href="https://learnopengl.com/PBR/Theory" target="_blank">previous</a> chapter that the Fresnel equation calculates just that (note the <code>clamp</code> here to prevent black spots):
    454 </p>
    455   
    456 <pre><code>
    457 vec3 fresnelSchlick(float cosTheta, vec3 F0)
    458 {
    459     return F0 + (1.0 - F0) * pow(clamp(1.0 - cosTheta, 0.0, 1.0), 5.0);
    460 }  
    461 </code></pre>
    462   
    463 <p>
    464   The Fresnel-Schlick approximation expects a <var>F0</var> parameter which is known as the <em>surface reflection at zero incidence</em> or how much the surface reflects if looking directly at the surface. The <var>F0</var> varies per material and is tinted on metals as we find in large material databases. In the PBR metallic workflow we make the simplifying assumption that most dielectric surfaces look visually correct with a constant <var>F0</var> of <code>0.04</code>, while we do specify <var>F0</var> for metallic surfaces as then given by the albedo value. This translates to code as follows:
    465 </p>
    466   
    467 <pre><code>
    468 vec3 F0 = vec3(0.04); 
    469 F0      = mix(F0, albedo, metallic);
    470 vec3 F  = fresnelSchlick(max(dot(H, V), 0.0), F0);
    471 </code></pre>
    472   
    473 <p>
    474   As you can see, for non-metallic surfaces <var>F0</var> is always <code>0.04</code>. For metallic surfaces, we vary <var>F0</var> by linearly interpolating between the original <var>F0</var> and the albedo value given the <var>metallic</var> property. 
    475 </p>
    476   
    477 <p>
    478   Given \(F\), the remaining terms to calculate are the normal distribution function \(D\) and the geometry function \(G\).
    479 </p>
    480   
    481 <p>
    482   In a direct PBR lighting shader their code equivalents are:
    483 </p>
    484   
    485 <pre><code>
    486 float DistributionGGX(vec3 N, vec3 H, float roughness)
    487 {
    488     float a      = roughness*roughness;
    489     float a2     = a*a;
    490     float NdotH  = max(dot(N, H), 0.0);
    491     float NdotH2 = NdotH*NdotH;
    492 	
    493     float num   = a2;
    494     float denom = (NdotH2 * (a2 - 1.0) + 1.0);
    495     denom = PI * denom * denom;
    496 	
    497     return num / denom;
    498 }
    499 
    500 float GeometrySchlickGGX(float NdotV, float roughness)
    501 {
    502     float r = (roughness + 1.0);
    503     float k = (r*r) / 8.0;
    504 
    505     float num   = NdotV;
    506     float denom = NdotV * (1.0 - k) + k;
    507 	
    508     return num / denom;
    509 }
    510 float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
    511 {
    512     float NdotV = max(dot(N, V), 0.0);
    513     float NdotL = max(dot(N, L), 0.0);
    514     float ggx2  = GeometrySchlickGGX(NdotV, roughness);
    515     float ggx1  = GeometrySchlickGGX(NdotL, roughness);
    516 	
    517     return ggx1 * ggx2;
    518 }
    519 </code></pre>
    520   
    521 <p>
    522   What's important to note here is that in contrast to the <a href="https://learnopengl.com/PBR/Theory" target="_blank">theory</a> chapter, we pass the roughness parameter directly to these functions; this way we can make some term-specific modifications to the original roughness value. Based on observations by Disney and adopted by Epic Games, the lighting looks more correct squaring the roughness in both the geometry and normal distribution function.
    523 </p>
    524   
    525 <p>
    526   With both functions defined, calculating the NDF and the G term in the reflectance loop is straightforward: 
    527 </p>
    528   
    529 <pre><code>
    530 float NDF = DistributionGGX(N, H, roughness);       
    531 float G   = GeometrySmith(N, V, L, roughness);       
    532 </code></pre>
    533   
    534 <p>
    535   This gives us enough to calculate the Cook-Torrance BRDF:
    536 </p>
    537   
    538 <pre><code>
    539 vec3 numerator    = NDF * G * F;
    540 float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0)  + 0.0001;
    541 vec3 specular     = numerator / denominator;  
    542 </code></pre>
    543   
    544 <p>
    545   Note that we add <code>0.0001</code> to the denominator to prevent a divide by zero in case any dot product ends up <code>0.0</code>.
    546 </p>
    547   
    548 <p>
    549   Now we can finally calculate each light's contribution to the reflectance equation. As the Fresnel value directly corresponds to \(k_S\) we can use <var>F</var> to denote the specular contribution of any light that hits the surface. From \(k_S\) we can then calculate the ratio of refraction \(k_D\):
    550 </p>
    551   
    552 <pre><code>
    553 vec3 kS = F;
    554 vec3 kD = vec3(1.0) - kS;
    555   
    556 kD *= 1.0 - metallic;	
    557 </code></pre>
    558   
    559 <p>
    560   Seeing as <var>kS</var> represents the energy of light that gets reflected, the remaining ratio of light energy is the light that gets refracted which we store as <var>kD</var>. Furthermore, because metallic surfaces don't refract light and thus have no diffuse reflections we enforce this property by nullifying <var>kD</var> if the surface is metallic.  This gives us the final data we need to calculate each light's outgoing reflectance value:
    561 </p>
    562   
    563 <pre><code>
    564     const float PI = 3.14159265359;
    565   
    566     float NdotL = max(dot(N, L), 0.0);        
    567     Lo += (kD * albedo / PI + specular) * radiance * NdotL;
    568 }
    569 </code></pre>
    570   
    571 <p>
    572   The resulting <var>Lo</var> value, or the outgoing radiance, is effectively the result of the reflectance equation's integral \(\int\) over \(\Omega\).  We don't really have to try and solve the integral for all possible incoming light directions as we know exactly the 4 incoming light directions that can influence the fragment. Because of this, we can directly loop over these incoming light directions e.g. the number of lights in the scene.
    573 </p>
    574   
    575 <p>
    576   What's left is to add an (improvised) ambient term to the direct lighting result <var>Lo</var> and we have the final lit color of the fragment:
    577 </p>
    578 
    579 <pre><code>
    580 vec3 ambient = vec3(0.03) * albedo * ao;
    581 vec3 color   = ambient + Lo;  
    582 </code></pre>
    583   
    584 <h3>Linear and HDR rendering</h3>
    585 <p>
    586   So far we've assumed all our calculations to be in linear color space and to account for this we need to <a href="https://learnopengl.com/Advanced-Lighting/Gamma-Correction" target="_blank">gamma correct</a> at the end of the shader. Calculating lighting in linear space is incredibly important as PBR requires all inputs to be linear. Not taking this into account will result in incorrect lighting. Additionally, we want light inputs to be close to their physical equivalents such that their radiance or color values can vary wildly over a high spectrum of values. As a result, <var>Lo</var> can rapidly grow really high which then gets clamped between <code>0.0</code> and <code>1.0</code> due to the default low dynamic range (LDR) output. We fix this by taking <var>Lo</var> and tone or exposure map the <a href="https://learnopengl.com/Advanced-Lighting/HDR" target="_blank">high dynamic range</a> (HDR) value correctly to LDR before gamma correction: 
    587 </p>
    588   
    589 <pre><code>
    590 color = color / (color + vec3(1.0));
    591 color = pow(color, vec3(1.0/2.2)); 
    592 </code></pre>
    593   
    594 <p>
    595   Here we tone map the HDR color using the Reinhard operator, preserving the high dynamic range of a possibly highly varying irradiance, after which we gamma correct the color. We don't have a separate framebuffer or post-processing stage so we can directly apply both the tone mapping and gamma correction step at the end of the forward fragment shader. 
    596 </p>
    597   
    598   <img src="/img/pbr/lighting_linear_vs_non_linear_and_hdr.png" alt="The difference linear and HDR rendering makes in an OpenGL PBR renderer."/>
    599   
    600 <p>
    601   Taking both linear color space and high dynamic range into account is incredibly important in a PBR pipeline. Without these it's impossible to properly capture the high and low details of varying light intensities and your calculations end up incorrect and thus visually unpleasing.
    602 </p>
    603   
    604 <h3>Full direct lighting PBR shader</h3>
    605 <p>
    606   All that's left now is to pass the final tone mapped and gamma corrected color to the fragment shader's output channel and we have ourselves a direct PBR lighting shader. For completeness' sake, the complete <fun>main</fun> function is listed below:
    607 </p>
    608   
    609 <pre><code>
    610 #version 330 core
    611 out vec4 FragColor;
    612 in vec2 TexCoords;
    613 in vec3 WorldPos;
    614 in vec3 Normal;
    615 
    616 // material parameters
    617 uniform vec3  albedo;
    618 uniform float metallic;
    619 uniform float roughness;
    620 uniform float ao;
    621 
    622 // lights
    623 uniform vec3 lightPositions[4];
    624 uniform vec3 lightColors[4];
    625 
    626 uniform vec3 camPos;
    627 
    628 const float PI = 3.14159265359;
    629   
    630 float DistributionGGX(vec3 N, vec3 H, float roughness);
    631 float GeometrySchlickGGX(float NdotV, float roughness);
    632 float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness);
    633 vec3 fresnelSchlick(float cosTheta, vec3 F0);
    634 
    635 void main()
    636 {		
    637     vec3 N = normalize(Normal);
    638     vec3 V = normalize(camPos - WorldPos);
    639 
    640     vec3 F0 = vec3(0.04); 
    641     F0 = mix(F0, albedo, metallic);
    642 	           
    643     // reflectance equation
    644     vec3 Lo = vec3(0.0);
    645     for(int i = 0; i &lt; 4; ++i) 
    646     {
    647         // calculate per-light radiance
    648         vec3 L = normalize(lightPositions[i] - WorldPos);
    649         vec3 H = normalize(V + L);
    650         float distance    = length(lightPositions[i] - WorldPos);
    651         float attenuation = 1.0 / (distance * distance);
    652         vec3 radiance     = lightColors[i] * attenuation;        
    653         
    654         // cook-torrance brdf
    655         float NDF = DistributionGGX(N, H, roughness);        
    656         float G   = GeometrySmith(N, V, L, roughness);      
    657         vec3 F    = fresnelSchlick(max(dot(H, V), 0.0), F0);       
    658         
    659         vec3 kS = F;
    660         vec3 kD = vec3(1.0) - kS;
    661         kD *= 1.0 - metallic;	  
    662         
    663         vec3 numerator    = NDF * G * F;
    664         float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.0001;
    665         vec3 specular     = numerator / denominator;  
    666             
    667         // add to outgoing radiance Lo
    668         float NdotL = max(dot(N, L), 0.0);                
    669         Lo += (kD * albedo / PI + specular) * radiance * NdotL; 
    670     }   
    671   
    672     vec3 ambient = vec3(0.03) * albedo * ao;
    673     vec3 color = ambient + Lo;
    674 	
    675     color = color / (color + vec3(1.0));
    676     color = pow(color, vec3(1.0/2.2));  
    677    
    678     FragColor = vec4(color, 1.0);
    679 }  
    680 </code></pre>
    681   
    682 <p>
    683   Hopefully, with the <a href="https://learnopengl.com/PBR/Theory" target="_blank">theory</a> from the previous chapter and the knowledge of the reflectance equation this shader shouldn't be as daunting anymore. If we take this shader, 4 point lights, and quite a few spheres where we vary both their metallic and roughness values on their vertical and horizontal axis respectively, we'd get something like this:
    684 </p>
    685   
    686   <img src="/img/pbr/lighting_result.png" alt="Render of PBR spheres with varying roughness and metallic values in OpenGL."/>
    687   
    688 <p>
    689   From bottom to top the metallic value ranges from <code>0.0</code> to <code>1.0</code>, with roughness increasing left to right from <code>0.0</code> to <code>1.0</code>. You can see that by only changing these two simple to understand parameters we can already display a wide array of different materials.
    690 </p>
    691 
    692 <iframe src="https://oneshader.net/embed/6b8a7c6363" style="width:80%; height:440px; border:0;margin-left:10.0%; margin-right:12.5%;" frameborder="0" allowfullscreen></iframe>
    693   
    694 <p>
    695   You can find the full source code of the demo <a href="/code_viewer_gh.php?code=src/6.pbr/1.1.lighting/lighting.cpp" target="_blank">here</a>.
    696 </p>
    697       
    698 <h2>Textured PBR</h2>
    699 <p>
    700   Extending the system to now accept its surface parameters as textures instead of uniform values gives us per-fragment control over the surface material's properties:
    701 </p>
    702 
    703 <pre><code>
    704 [...]
    705 uniform sampler2D albedoMap;
    706 uniform sampler2D normalMap;
    707 uniform sampler2D metallicMap;
    708 uniform sampler2D roughnessMap;
    709 uniform sampler2D aoMap;
    710   
    711 void main()
    712 {
    713     vec3 albedo     = pow(texture(albedoMap, TexCoords).rgb, 2.2);
    714     vec3 normal     = getNormalFromNormalMap();
    715     float metallic  = texture(metallicMap, TexCoords).r;
    716     float roughness = texture(roughnessMap, TexCoords).r;
    717     float ao        = texture(aoMap, TexCoords).r;
    718     [...]
    719 }
    720 </code></pre>
    721   
    722 <p>
    723   Note that the albedo textures that come from artists are generally authored in sRGB space which is why we first convert them to linear space before using albedo in our lighting calculations. Based on the system artists use to generate ambient occlusion maps you may also have to convert these from sRGB to linear space as well. Metallic and roughness maps are almost always authored in linear space.
    724 </p>
    725   
    726 <p>
    727   Replacing the material properties of the previous set of spheres with textures, already shows a major visual improvement over the previous lighting algorithms we've used:
    728 </p>
    729   
    730   <img src="/img/pbr/lighting_textured.png" alt="Render of PBR spheres with a textured PBR material in OpenGL."/>
    731   
    732 <p>
    733   You can find the full source code of the textured demo <a href="/code_viewer_gh.php?code=src/6.pbr/1.2.lighting_textured/lighting_textured.cpp" target="_blank">here</a> and the texture set used <a href="http://freepbr.com/materials/rusted-iron-pbr-metal-material-alt/" target="_blank">here</a> (with a white ao map). Keep in mind that metallic surfaces tend to look too dark in direct lighting environments as they don't have diffuse reflectance. They do look more correct when taking the environment's specular ambient lighting into account, which is what we'll focus on in the next chapters.
    734 </p>
    735   
    736 <p>
    737   While not as visually impressive as some of the PBR render demos you find out there, given that we don't yet have <a href="https://learnopengl.com/PBR/IBL/Diffuse-irradiance" target="_blank">image based lighting</a> built in, the system we have now is still a physically based renderer, and even without IBL you'll see your lighting look a lot more realistic.
    738 </p>       
    739 
    740     </div>
    741     
    742 	</main>
    743 </body>
    744 </html>