Multiple-lights.html (25553B)
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">Multiple lights</h1> 319 <h1 id="content-url" style='display:none;'>Lighting/Multiple-lights</h1> 320 <p> 321 In the previous chapters we learned a lot about lighting in OpenGL. We learned about Phong shading, materials, lighting maps and different types of light casters. In this chapter we're going to combine all the previously obtained knowledge by creating a fully lit scene with 6 active light sources. We are going to simulate a sun-like light as a directional light source, 4 point lights scattered throughout the scene and we'll be adding a flashlight as well. 322 </p> 323 324 <p> 325 To use more than one light source in the scene we want to encapsulate the lighting calculations into GLSL <def>functions</def>. The reason for that is that the code quickly gets nasty when we do lighting computations with multiple light types, each requiring different computations. If we were to do all these calculations in the <fun>main</fun> function only, the code quickly becomes difficult to understand. 326 </p> 327 328 <p> 329 Functions in GLSL are just like C-functions. We have a function name, a return type and we need to declare a prototype at the top of the code file if the function hasn't been declared yet before the main function. We'll create a different function for each of the light types: directional lights, point lights and spotlights. 330 </p> 331 332 <p> 333 When using multiple lights in a scene the approach is usually as follows: we have a single color vector that represents the fragment's output color. For each light, the light's contribution to the fragment is added to this output color vector. So each light in the scene will calculate its individual impact and contribute that to the final output color. A general structure would look something like this: 334 </p> 335 336 <pre><code> 337 out vec4 FragColor; 338 339 void main() 340 { 341 // define an output color value 342 vec3 output = vec3(0.0); 343 // add the directional light's contribution to the output 344 output += someFunctionToCalculateDirectionalLight(); 345 // do the same for all point lights 346 for(int i = 0; i < nr_of_point_lights; i++) 347 output += someFunctionToCalculatePointLight(); 348 // and add others lights as well (like spotlights) 349 output += someFunctionToCalculateSpotLight(); 350 351 FragColor = vec4(output, 1.0); 352 } 353 </code></pre> 354 355 <p> 356 The actual code will likely differ per implementation, but the general structure remains the same. We define several functions that calculate the impact per light source and add its resulting color to an output color vector. If for example two light sources are close to the fragment, their combined contribution would result in a more brightly lit fragment compared to the fragment being lit by a single light source. 357 </p> 358 359 <h2>Directional light</h2> 360 <p> 361 We want to define a function in the fragment shader that calculates the contribution a directional light has on the corresponding fragment: a function that takes a few parameters and returns the calculated directional lighting color. 362 </p> 363 364 <p> 365 First we need to set the required variables that we minimally need for a directional light source. We can store the variables in a struct called <fun>DirLight</fun> and define it as a uniform. The struct's variables should be familiar from the <a href="https://learnopengl.com/Lighting/Light-casters" target="_blank">previous</a> chapter: 366 </p> 367 368 <pre><code> 369 struct DirLight { 370 vec3 direction; 371 372 vec3 ambient; 373 vec3 diffuse; 374 vec3 specular; 375 }; 376 uniform DirLight dirLight; 377 </code></pre> 378 379 <p> 380 We can then pass the <var>dirLight</var> uniform to a function with the following prototype: 381 </p> 382 383 <pre><code> 384 vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir); 385 </code></pre> 386 387 <note> 388 Just like C and C++, when we want to call a function (in this case inside the <fun>main</fun> function) the function should be defined somewhere before the caller's line number. In this case we'd prefer to define the functions below the <fun>main</fun> function so this requirement doesn't hold. Therefore we declare the function's prototypes somewhere above the <fun>main</fun> function, just like we would in C. 389 </note> 390 391 <p> 392 You can see that the function requires a <fun>DirLight</fun> struct and two other vectors required for its computation. If you successfully completed the previous chapter then the content of this function should come as no surprise: 393 </p> 394 395 <pre><code> 396 vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) 397 { 398 vec3 lightDir = normalize(-light.direction); 399 // diffuse shading 400 float diff = max(dot(normal, lightDir), 0.0); 401 // specular shading 402 vec3 reflectDir = reflect(-lightDir, normal); 403 float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); 404 // combine results 405 vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); 406 vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); 407 vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); 408 return (ambient + diffuse + specular); 409 } 410 </code></pre> 411 412 <p> 413 We basically copied the code from the previous chapter and used the vectors given as function arguments to calculate the directional light's contribution vector. The resulting ambient, diffuse and specular contributions are then returned as a single color vector. 414 </p> 415 416 <h2>Point light</h2> 417 <p> 418 Similar to directional lights we also want to define a function that calculates the contribution a point light has on the given fragment, including its attenuation. Just like directional lights we want to define a struct that specifies all the variables required for a point light: 419 </p> 420 421 <pre><code> 422 struct PointLight { 423 vec3 position; 424 425 float constant; 426 float linear; 427 float quadratic; 428 429 vec3 ambient; 430 vec3 diffuse; 431 vec3 specular; 432 }; 433 #define NR_POINT_LIGHTS 4 434 uniform PointLight pointLights[NR_POINT_LIGHTS]; 435 </code></pre> 436 437 <p> 438 As you can see we used a pre-processor directive in GLSL to define the number of point lights we want to have in our scene. We then use this <var>NR_POINT_LIGHTS</var> constant to create an array of <fun>PointLight</fun> structs. Arrays in GLSL are just like C arrays and can be created by the use of two square brackets. Right now we have 4 <fun>PointLight</fun> structs to fill with data. 439 </p> 440 441 <!--<note> 442 We could also simply define <strong>one</strong> large struct (instead of different structs per light type) that contains all the necessary variables for <strong>all</strong> the different light types and use that struct for each function, and simply ignore the variables we don't need. However, I personally find the current approach more intuitive and aside from a few extra lines of code it could save up some memory since not all light types need all variables. 443 </note>--> 444 445 <p> 446 The prototype of the point light's function is as follows: 447 </p> 448 449 <pre><code> 450 vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir); 451 </code></pre> 452 453 <p> 454 The function takes all the data it needs as its arguments and returns a <code>vec3</code> that represents the color contribution that this specific point light has on the fragment. Again, some intelligent copy-and-pasting results in the following function: 455 </p> 456 457 <pre><code> 458 vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) 459 { 460 vec3 lightDir = normalize(light.position - fragPos); 461 // diffuse shading 462 float diff = max(dot(normal, lightDir), 0.0); 463 // specular shading 464 vec3 reflectDir = reflect(-lightDir, normal); 465 float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); 466 // attenuation 467 float distance = length(light.position - fragPos); 468 float attenuation = 1.0 / (light.constant + light.linear * distance + 469 light.quadratic * (distance * distance)); 470 // combine results 471 vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); 472 vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords)); 473 vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); 474 ambient *= attenuation; 475 diffuse *= attenuation; 476 specular *= attenuation; 477 return (ambient + diffuse + specular); 478 } 479 </code></pre> 480 481 <p> 482 Abstracting this functionality away in a function like this has the advantage that we can easily calculate the lighting for multiple point lights without the need for duplicated code. In the <fun>main</fun> function we simply create a loop that iterates over the point light array that calls <fun>CalcPointLight</fun> for each point light. 483 </p> 484 485 <h2>Putting it all together</h2> 486 <p> 487 Now that we defined both a function for directional lights and a function for point lights we can put it all together in the <fun>main</fun> function. 488 </p> 489 490 <pre><code> 491 void main() 492 { 493 // properties 494 vec3 norm = normalize(Normal); 495 vec3 viewDir = normalize(viewPos - FragPos); 496 497 // phase 1: Directional lighting 498 vec3 result = CalcDirLight(dirLight, norm, viewDir); 499 // phase 2: Point lights 500 for(int i = 0; i < NR_POINT_LIGHTS; i++) 501 result += CalcPointLight(pointLights[i], norm, FragPos, viewDir); 502 // phase 3: Spot light 503 //result += CalcSpotLight(spotLight, norm, FragPos, viewDir); 504 505 FragColor = vec4(result, 1.0); 506 } 507 </code></pre> 508 509 <p> 510 Each light type adds its contribution to the resulting output color until all light sources are processed. The resulting color contains the color impact of all the light sources in the scene combined. We leave the <fun>CalcSpotLight</fun> function as an exercise for the reader. 511 </p> 512 513 <note> 514 There are lot of duplicated calculations in this approach spread out over the light type functions (e.g. calculating the reflect vector, diffuse and specular terms, and sampling the material textures) so there's room for optimization here. 515 </note> 516 517 <p> 518 Setting the uniforms for the directional light struct shouldn't be too unfamiliar, but you may be wondering how to set the uniform values of the point lights since the point light uniform is actually an array of <fun>PointLight</fun> structs. This isn't something we've discussed before. 519 </p> 520 521 <p> 522 Luckily for us, it isn't too complicated. Setting the uniform values of an array of structs works just like setting the uniforms of a single struct, although this time we also have to define the appropriate index when querying the uniform's location: 523 </p> 524 525 <pre><code> 526 lightingShader.setFloat("pointLights[0].constant", 1.0f); 527 </code></pre> 528 529 <p> 530 Here we index the first <fun>PointLight</fun> struct in the <var>pointLights</var> array and internally retrieve the location of its <var>constant</var> variable, which we set to <code>1.0</code>. 531 </p> 532 533 <p> 534 Let's not forget that we also need to define a position vector for each of the 4 point lights so let's spread them up a bit around the scene. We'll define another <code>glm::vec3</code> array that contains the pointlights' positions: 535 </p> 536 537 <pre><code> 538 glm::vec3 pointLightPositions[] = { 539 glm::vec3( 0.7f, 0.2f, 2.0f), 540 glm::vec3( 2.3f, -3.3f, -4.0f), 541 glm::vec3(-4.0f, 2.0f, -12.0f), 542 glm::vec3( 0.0f, 0.0f, -3.0f) 543 }; 544 </code></pre> 545 546 <p> 547 Then we index the corresponding <fun>PointLight</fun> struct from the <var>pointLights</var> array and set its <var>position</var> attribute as one of the positions we just defined. Also be sure to now draw 4 light cubes instead of just 1. Simply create a different model matrix for each of the light objects just like we did with the containers. 548 </p> 549 550 <p> 551 If you'd also use a flashlight, the result of all the combined lights looks something like this: 552 </p> 553 554 <img src="/img/lighting/multiple_lights_combined.png" class="clean"/> 555 556 <p> 557 As you can see there appears to be some form of a global light (like a sun) somewhere in the sky, we have 4 lights scattered throughout the scene and a flashlight is visible from the player's perspective. Looks pretty neat doesn't it? 558 </p> 559 560 <p> 561 You can find the full source code of the final application <a href="/code_viewer_gh.php?code=src/2.lighting/6.multiple_lights/multiple_lights.cpp" target="_blank">here</a>. 562 </p> 563 564 <p> 565 The image shows all the light sources set with the default light properties we've used in the previous chapters, but if you play around with these values you can get pretty interesting results. Artists and level designers generally tweak all these lighting variables in a large editor to make sure the lighting matches the environment. Using our simple environment you can already create some pretty interesting visuals simply by tweaking the lights' attributes: 566 </p> 567 568 <img src="/img/lighting/multiple_lights_atmospheres.png" class="clean" style="border-radius: 0px;"/> 569 570 <p> 571 We also changed the clear color to better reflect the lighting. You can see that by simply adjusting some of the lighting parameters you can create completely different atmospheres. 572 </p> 573 574 <p> 575 By now you should have a pretty good understanding of lighting in OpenGL. With the knowledge so far we can already create interesting and visually rich environments and atmospheres. Try playing around with all the different values to create your own atmospheres. 576 </p> 577 578 <h2>Exercises</h2> 579 <p> 580 <ul> 581 <li>Can you (sort of) re-create the different atmospheres of the last image by tweaking the light's attribute values? <a href="/code_viewer_gh.php?code=src/2.lighting/6.multiple_lights_exercise1/multiple_lights_exercise1.cpp" target="_blank">solution</a>.</li> 582 </ul> 583 </p> 584 585 </div> 586 587 </main> 588 </body> 589 </html>